Introduction

Welcome to the Windows 7 world. I will try here to demonstrate a collection of seven, most appealing programming features that you can use to enhance both the appearance and the internals of your application!

This article is a candidate for the CodeProject Windows 7 contest. If you like this, please vote for me!

The Magic Number Seven

The article discusses these topics:

All these are used for the sample application, "Windows 7 Downloader", which downloads files. It uses the taskbar extensions to display a progress bar and toolbar, it stores the files to the library locations, draws progress and information with Direct2D, allows touching the screen to select a transfer, animates the transfer progress with the animation manager, changes the drawing depending on light conditions (using the Virtual Light Sensor tool from the SDK), and calculates and displays your location from a GPS sensor, if installed, drawing a virtual path from your location to the destination file's location using Google Maps. Finally, all this is presented with a ribbon interface.

Code pasted in this article is stripped of error checking functions for the sake of simplicity. You should always be checking the HRESULT values of the functions! Also, most known code (like the IUnknown implementation) is stripped from here as well. To get the full code, see the CPP files.

Includes:

All-in-one sources

TBAR.CPP - Taskbar extensions

LIBR.CPP - Libraries

TOUCH.CPP - Touch sample

SENS.CPP - Sensor Explorer

ANIM.CPP - Animation manager

D2D.CPP - Direct2D sample

RIB.CPP - Ribbon

MAIN.CPP/RC/Hs - Main stuff

Ribbon bitmaps

Visual Studio 2008 solution and project files (VCPROJ and SLN)

Visual Ribbon Creator project file (VRC)

x86/x64 executables

Required Software

Windows 7. If you still have RC1, most of the code will work with it - however, some definitions have changed from the RC1 SDK.

Optional/Helpful Software

GPS Sensor Driver - my driver to test the Sensor and Location API, with real data coming from any NMEA-compatible GPS that can connect via a serial port (USB or Bluetooth). If you do not own an actual GPS hardware, this driver can also simulate your location.

CodePlex Touch Simulation. If you do not have a touch screen yet, this one contains a driver that can simulate touch gestures with the mouse.

The APIs in General

But from a few exceptions, all APIs discussed in this article are COM features. That is, your application will continue to work even if not running under Windows 7 - so there is almost no reason not to implement them!

There are a few functions (for example, the one to create the Direct2D interface) that you should call them LoadLibrary/GetProcAddress - otherwise, your application will fail to initialize when not running under Windows 7.

Some of the features (in particular, Ribbon, Direct2D, and the Animation Manager) are also available in Vista through the Platform update for Vista SP2.

What are they?

Specify recent/frequent items for the taskbar right click (Jumplists).

Display a toolbar to manipulate the application without switching to the application.

Handle the taskbar as a progress bar.

Fig 1.1: Jumplists

Fig 1.2: Toolbar

Fig 1.3: Progress bar

Important note #1: All manipulation of the taskbar must occur after your application knows that the taskbar button has been created. Call RegisterWindowMessage(L"TaskbarButtonCreated") to get a message ID, and when you receive this in your loop, then you do the taskbar stuff. Failure to do this will cost you some hours of debugging, as it happened to me

Important note #2: If you are running the application as administrator, a manipulation toolbar cannot be used since the Explorer runs in medium integrity and can't therefore send messages to your application. If you do want the Explorer to be able to communicate with your application while in High Integrity mode, you must use ChangeWindowMessageFilterEx for both WM_COMMAND and the message returned by RegisterWindowMessage(L"TaskbarButtonCreated").

Step by Step Recent Items (Error handling removed for simplicity):

There are two known categories for which you do not need to define something, the recent and the frequent items. These items must be filenames for these categories. If you want to add any other sort of category, you have to implement your own IObjectArray. For more, see the ICustomDestinationList::AppendCategory documentation.

Read More

What are libraries?

A Library is a way to tell Windows 7 where your files are stored. The problem with the "My Documents" folder is that it is only one; a user might want to store important files all over the hard disk. A Library tells Windows where your collection is, so it can be easily indexed and searched. So a Library can have many files, sorted as they were in a single folder, while, in reality, they are in different directories in the hard disk. It is basically a container of multiple directories; this reduces application complexity - for example, an application that needs to be notified when something in the files is changed, now only needs to monitor the library object, which automatically monitors all the items it contains.

For example, one might want to have all their photos from the Island of Crete in special locations (depending on when they visited the Island), but to be able to search them all at once, they will simply define a Library with all the photos, and searching and indexing and sorting of these photos will occur as they belonged to a single directory.

Because the Libraries are a Shell interface, it can be used with any sort of application, not just from our downloader.

Enabling the Selection of a Library in Save-as:

To enable the usage of libraries, you can normally call GetSaveFileName(), but for more flexibility, you can use IFileSaveDialog:

Creating and Using a Library

Our application creates a "W7DL" Library. For each file you download with it (the default location is My documents\\W7DL), its directory is stored as an item in that Library so you can access all your downloads in a single destination:

Hey, does anyone have a Touch screen yet?

I am not sure, but I have good news for you. You can use your mouse (or multiple mice) to simulate touching, with the aid of the Codeplex Touch Simulation tool. This tool converts your mice to virtual touching devices. This video shows how to install and enable this simulator. Note that you must "disable" the mice function for the application to actually receive WM_TOUCH.

Registration for WM_TOUCH

A window must register to be capable to receive WM_TOUCH with RegisterTouchWindow. If that window has child windows, RegisterTouchWindow() must be called for each of them separately.

Processing WM_TOUCH

The wParam low-word contains the number of touches, and lParam contains a handle. You use this handle to GetTouchInputInfo to get the touch information in an array of TOUCHINPUT structures. You must free this handle with CloseTouchInputHandle, or pass the message to DefWindowProc for the cleanup.

The TOUCHINPUT structure contains many elements. For the sake of simplicity, here, you will convert the screen coordinates to the client coordinates of our app. If a download is "touched", it is selected.

Read More

What is this?

The Sensor API is a new abstraction API to query values from sensors, i.e., devices that can generate data from a hardware source. Sensors can include GPS, Light Detectors, Temperature Detectors, Motion Detectors, Biometrics (fingerprint), and other stuff.

You can think of the Sensor API as a "raw input" method, from which you can get information from any device that has a sensor driver no matter what device that is.

The Locaton API is a reduction of the Sensor API which gets the PC's location from a GPS Sensor, if installed. It is useful if you only want to get an idea of the PC's location, and you do not need more data like satellite location, speed etc., which would also be returned by a GPS sensor.

Because sensors can provide user-sensitive data, sensors are disabled by default. An application can request to use a sensor, and this results in a Control Panel message to prompt the user to allow the sensor. You cannot enable sensor access programmatically even if running as Administrator. The user can also specify, through the Control Panel, which applications have access to selected sensors.

Figure 4.1: Permission dialog displayed when an application tries to access a sensor.

To use a sensor, a sensor driver must be installed. The SDK comes with a "Virtual Light Sensor" which you can use to simulate a light detector sensor. You can also use my own GPS Sensor Driver, a driver to test the Sensor and Location API with real data coming from any NMEA-compatible GPS that can connect via a serial port (USB or Bluetooth). If you do not own an actual GPS hardware, this driver can also simulate your location.

The application uses the Sensor API to query the virtual light sensor and to adjust the foreground and background colors of the client area (the more light, the more bold the letters appear). The application uses the Location API to query your PC for its location, and then it can display a map (using Google Maps) along with the destinations of your downloads (IPs are converted to GPS coordinates by using the http://www.hostip.info/ free service).

The meaning of the sensor values depend on the sensor type. There are some predefined types and categories you can use, but it could be any category and type CLSID, as long as you know how to interpret it. For a custom sensor driver project, you can see my own 3DConnexion Sensor driver which maps as a sensor and a 3D Mouse 3DConnexion device.

It is very important to note that the ILocation interface is volatile. That is, you cannot get an ILocation and use it throughout the application while the sensor information might change. For safety, you should release the ILocation as long as it is not needed, and when you need Location Information again, instantiate it.

This driver can use your GPS hardware (such as a Bluetooth GPS device), or simulate the information if no hardware exists.

Sensor Drivers

A sensor driver is a user mode driver (UMDF) that provides sensor information to user applications. The Windows 7 Device Driver Kit provides the "SensorSkeleton" driver which you can use as a template to implement your own Sensor driver.

Read More

What is this?

The Animation Manager is a new API to manipulate animations. It does not actually draw anything. Instead, it allows you to specify variables (objects) to animate, and a storyboard, which contains the variables to animate and the type of the animation to use (there are defined animations, and you can also define your own). Finally, the animation is done through either a timer (which we will be using here), or when triggered by the application itself:

Fig. 5.1 : Sine animation while downloading.

So, using this API, you simply specify what to animate and how, and then you are given back the results of the math applied to your variables, and then you can use them to draw your objects. For example, if you have a 3D cube that rotates based on X,Y,Z values, you can define these to be manipulated linearly, logarithmically, or in another predefined or custom way, and then your callback functions are called with the results, based on a frame rate timer.

For example, this application draws and animates a sine function during the download. So it needs two variables: the "x", which linearly goes from 0 to 100 , and "y" , which is a sinusoid function that goes from -1 to 1. When values are updated (based on a timer), the application redraws the sine.

Quick Steps

The Animation API is a powerful API, and space here permits only a brief discussion. In short, here is what you have to do for a timer-driven application animation:

After the above code, the animation starts for 5 seconds (as we have specified in the transitions). The X variable starts from 0 and goes to 100 (linearly), the Y variable starts from 0 and goes to 1.0 with a period of 0.5f (or 2Hz frequency). The application uses these values to animate a sine function while the download is in progress, while using color transparency to indicate the percentage of the download.

The Animation Manager allows you to use key frames to setup loop positions. For more information on this, see Creating a Storyboard.

Read More

The Direct2D is a powerful ActiveX hardware-accelerated drawing API which replaces GDI and GDI+, providing enhanced features. The application draws its client area through Direct2D; however, the real power of this API is shown in applications that draw a lot of information, scroll in real time etc. An example of that kind of application is my Turbo Play. Direct2D also supports software rendering if hardware acceleration is not available. Direct2D can write to both an HWND or an HDC, allowing you to combine it, if needed, with GDI or GDI+. In addition, DirectWrite is provided to write high-quality text to the target.

Use the Render Target interface to draw. Call the method BeginDraw() before drawing, draw the data with the ID2D1HwndRenderTarget or ID2D1DCRenderTarget members, then use the method Flush() or EndDraw() to end the drawing. Flush() will draw the result even if errors occur, while EndDraw() will draw the result only if no errors have occurred. Check the return value of EndDraw() - you may need to recreate the Direct2D object.

When your window is resized, call the method Resize() to update the Direct2D interface.

You should call the CreateFactory functions with LoadLibrary()/GetProcAddress to ensure that your application will run in previous OS versions.

Note that there is no "pen" ; each function that draws shapes accepts a stroke style and width.

Direct2D Images

The easiest way to supply an image to Direct2D is through the Windows Imaging Component. WIC can convert an HBITMAP to an IWICBitmap* and then we use the render target's CreateBitmapFromWicBitmap() to convert it to a format that Direct2D likes. If direct conversion fails, then we can convert it to the 32bppPBGRA format using WIC:

Using this function is easy, but in practice, you should convert all your HBITMAPs to ID2D1Bitmap* once and then use them directly with DrawBitmap(), to avoid unnecessary overhead each time the bitmap has to be drawn.

Direct2D Shapes

Use methods exposed from ID2D1RenderTarget (which is the parent class of ID2D1HwndRenderTarget and ID2D1DCRenderTarget) to draw:

Draw/FillEllipse

DrawLine

Draw/FillRectangle

DrawText

This class supports many other forms of drawing, like layers, paths, mesh etc.

Direct2D Polygons

The following sample shows how to use DrawGeometry() to create a polygon by a set of points:

Read More

The last chapter of this article describes the most important added API to the Win32 collection, in my opinion. The Windows 7 Ribbon effectively transforms your application from "old" style to "new" style. Let's be honest; an application that uses Direct2D, Sensors, Taskbar Lists etc., won't be much noticed by the average-user as a serious upgrade; but a ribbon which replaces the old application toolbar and menu will definitely draw attention.

Fig 7.1: Visual Ribbon Creator Ribbon.

Basically, the ribbon is an area that contains

An application menu

A quick toolbar

An optional help button

Tabs

A tab is an area that contains groups of other controls. These controls can be buttons, checkboxes, dropdown combos, font selection/color selection controls, drop down buttons, and more. All this is stored in an XML configuration file, and it's compiled to a binary file with an SDK tool called UICC.EXE. UICC.exe can also generate .h and .rc files to include them to your existing .rc script, so the images for the ribbon are loaded. The ribbon only supports 32-bit BMP images.

A ribbon tab can be permanently displayed, or displayed optionally. Tabs and groups support "ApplicationMode", which is simply a binary value that indicates the bits that should be on for the tab or group to be displayed. So, a tab with ApplicationMode == 0 will be always displayed, whereas a tab with ApplicationMode == 3 (11b) will be displayed when the mode bit 0 or 1 is set.

A ribbon can contain more complex items:

Fig 7.2 : MS Paint Ribbon.

Tabs can also be "contextual". This is similar to the application mode, but there is a special focus to the tab so the user notices that there is additional content available, depending on the application context:

Fig 7.3: The Turbo Play contextual ribbon is displayed only if an MIDI music track is selected, to show the Score Editor.

Quick Steps:

Prepare the ribbon XML. You can either edit that file with Notepad, or have a tool (such as VRC) generate the binary for you. For more details on the XML format the ribbon wants, see my my Ribbon article in CodeProject.

In IUIApplication::OnViewChanged(), check for typeID == UI_VIEWTYPE_RIBBON and verb == UI_VIEWVERB_CREATE, then query the passed IUnknown interface for an IUIRibbon.*

In IUIApplication::OnViewChanged(), check for typeID == UI_VIEWTYPE_RIBBON and verb == UI_VIEWVERB_SIZE, and call IUIRibbon::GetHeight() to get the ribbon height. Each time the ribbon is resized, this callback is called.

Each time a ribbon element needs information from you, the IUICommandHandler::UpdateProperty is called. If you want to force a property to be updated, you call IUIFramework::InvalidateUICommand().

Each time a command is issued (e.g., a button is pressed), IUICommandHandler::Execute is called.

The IUIRibbon interface also allows you to save/load persisting ribbon size to/from a stream.

Step by Step Ribbon Creation

Implementation of the Command Handler, implementation of IUIApplication, Ribbon creation, and initialization:

Setting/Querying Properties

To set command properties, you first invalidate the command with InvalidateUICommand. This causes the ribbon to call your UpdateProperty method, in which you can test which value requests need changing. For example, you would need to check for propertykey UI_PKEY_Enabled to enable or disable commands, UI_PKEY_RecentItems to change the recent items, UI_PKEY_ContextAvailable to set the contextual tabs etc. Here are all the state properties.

Read More

Possible Bugs?

This is a list of bugs I have found so far in the Windows 7 APIs. Perhaps, these are my code's bugs; feel free to comment and advise me.

ILocation throws exceptions when used repeatedly or from a callback. For safety, use it immediately when needed and then release it.

ILocation does not return the Z (altitude), even if this property is properly returned from the sensor driver.

Limitations

The ribbon can't be dynamically generated or changed by an application, since UICC is required.

Sensor gets permanently disabled for the app once you say "no" to the sensor dialog.

Ribbon only accepts 24-bit bitmaps. It should allow JPGs, PNGs etc.

Ribbon forces to have the images inside the same module that contains the ribbon. This forces you to re-include the images to each localized DLL you make.

Ribbon does not notify you when the user selects a tab, and you cannot reorder the tabs in runtime.

You cannot remove items from the recent document list programmatically.

For the recent items to appear in the jump list, your application must have registered as the default opener for the extension. For example, if you open *.JXX files, you must register the JXX extension to open with your application in order for the jumplist to include recent files of that type.

Acknowledgements

Special thanks to these Microsoft staff that cooperated with me to test the various Windows 7 features: