In-Depth

Windows 7 Native Development with Visual Studio 2010

New APIs and native support for resources like the Ribbon UI, the Windows 7 Taskbar and parallelism boost native development.

You don't need to work in .NET to take advantage of some of the most noteworthy features of Windows 7. You can take full advantage of capabilities like Jump Lists and file preview/search in your Microsoft Foundation Classes (MFC) application, whether you're upgrading it to Visual Studio 2010 or starting a brand-new project. Visual C++ 2010 ships with many enhancements to MFC and the Active Template Library (ATL) that target Windows 7 out of the box. The Visual C++ IDE also has important enhancements to make the day-to-day development of modern Windows applications more productive.

To get started, all you need to do is take your MFC application and recompile it in Visual C++ 2010 or fire up the new Project Wizard and create a new MFC application.

MFC 10 Applications "Light Up" to Windows 7
After you compile your code with the latest version of MFC, the application is already prepared to showcase some of the new Windows 7 functionality -- that is, without any new lines of code yet. Let's look closer at some of the new features.

First thing, your application becomes DPI-aware. This means it will integrate with the Windows 7 High-DPI settings that enable users to keep their displays at the native resolution of the hardware while still making fonts and the UI appear bigger on the screen. In contrast, the UI of an application that's not High-DPI-aware will be stretched to appear larger, which can create a pixelated effect on the images or blur the text, reducing its readability.

MFC controls are High-DPI-aware and, as a result of declaring the application High-DPI-aware, they'll render correctly under these conditions. If your application does any custom drawing, you may still want to validate its look and feel under each available DPI setting. You can temporarily disable High-DPI support in your application by going to the project's Property Pages | Manifest Tool | Input and Outputs | Enable DPI Awareness and setting it to No.

Figure 1 shows a tab-based MFC application for which the Windows 7 Taskbar automatically shows not only a thumbnail of the windows but also a thumbnail of each currently open document. MFC applications tell the Windows 7 Taskbar how many documents are open and provide a live thumbnail preview of their content without any new code required on your side.


[Click on image for larger view.]
Figure 1. MFC applications automatically provide Taskbar previews for each document and tab opened.

If your application has one or more file extensions associated with it and the implementation is using common dialog boxes to open files, the Windows Taskbar Jump List (displayed when right-clicking on the application Taskbar button) will also automatically get populated with the most recent documents the user opened using the application. I'll talk more about how you can customize this list later on in this article. For now, let's focus our attention on your application mainframe.

Modernize Your Application Using the Windows Ribbon
An important decision you need to make as you migrate your app to Windows 7 is whether you want it to use the new Windows 7 Ribbon. Windows 7 features the Ribbon interface throughout the OS, enabling improved UI development on the platform. Whether your application is already using the MFC Ribbon control or not,

Visual C++ 2010 helps with migrating to the new Windows 7 Ribbon.
If your application is using menus and toolbars, migrating to the Ribbon requires some planning. For that, I recommend reading some of the documentation available on the philosophy behind the Microsoft Office Fluent UI, the guidelines of designing a new Ribbon-centric UI and the licensing requirements that come with using the Ribbon (see "Windows Ribbon Resources").

To get a better idea of how your Ribbon UI will look, the new Ribbon Designer in Visual Studio 2010 helps create the Ribbon and wire it to new and existing event handlers in your code. This is shown in Figure 2. Because the MFC Ribbon control is using the same WM_COMMAND-based event handlers, you can reuse the command IDs exposed previously by the menu and toolbars and streamline the migration.


[Click on image for larger view.]
Figure 2. The Visual Studio 2010 Ribbon Designer.

The Ribbon Designer stores the serialized representation of the Ribbon as a resource in XML format. At runtime, use the CMFCRibbonBar control to load the XML and recreate the Ribbon controls, as shown here:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{	...
 m_wndRibbonBar.Create(this); 
 // m_wndRibbonBar is a protected member 
 // of type CMFCRibbonBar
 m_wndRibbonBar.LoadFromResource(IDR_RIBBON); 
 // IDR_RIBBON is the resource created 
 // by the Ribbon Designer
	...
}

Office Ribbon support first shipped as part of the Visual C++ 2008 Feature Pack and later as part of the Visual Studio 2008 SP1. If you already spent time redesigning your UI to incorporate the Ribbon and your application is using the MFC Ribbon with the Office look and feel, in Visual Studio 2010 all you need to do is change the look and feel of your Ribbon to Windows 7 by adding two lines of code to your mainframe initialization code:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{	...
 CMFCVisualManager::SetDefaultManager(
  RUNTIME_CLASS(CMFCVisualManagerWindows7));
 m_wndRibbonBar.SetWindows7Look(TRUE);
	...
}

If you're upgrading your Ribbon from Visual Studio 2008, your existing application doesn't use the new declarative way of defining the Ribbon (the XML representation supported by Ribbon Designer).

To use the Ribbon Designer in your development, you need to replace your Ribbon creation code with the declarative XML-based creation. MFC helps you make this substitution by allowing you to save an already-created Ribbon (regardless of the approach used) to XML. Just add the following line below your Ribbon creation code and run your application:

m_wndRibbonBar.SaveToXMLFile(
 L"my2008ribbon.mfcribbon-ms");

This call will generate an XML serialization of the runtime representation of the Ribbon at the time of the call. You can then add the file to your MFC application as a Ribbon resource and replace all the creation code with one call to CMFCRibbonBar::LoadFromResource.

Enable Multi-Touch
Touch and multi-touch features provide an intuitive way for users to interact with PCs, and enable multiple input events to occur at the same time from different touch points. Based on these, complex gesture events are recognized by the OS or your app. Consider the implications of responding to multiple input events and how to customize app behavior if multi-touch-capable hardware is available. Verify there's a multi-touch-enabled device on the system by checking system metrics SM_DIGITIZER and SM_MAXIMUMTOUCHES:

BYTE digitizerStatus = (BYTE) GetSystemMetrics(
  SM_DIGITIZER);
if ((digitizerStatus & 
  (NID_READY + NID_MULTI_INPUT)) == 0) 
    //Stack Ready + MultiTouch
{
    MessageBox(L"No touch input is currently available.");
}
else
{
 BYTE nInputs = 
  (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);
    
 CString str;
 str.Format(
 L"Touch input available with %d touch points.", 
   nInputs);
 MessageBox(str);
}

Windows 7 consumes the low-level touch messages to recognize the gesture and sends it to a consuming window. There are six gestures that your application can respond to: zoom, pan, rotate, press, tap and two-finger tap. You can handle any gesture event in your MFC application by overriding the corresponding virtual method of any class derived from CWnd (like CMainFrame in the following example):

BOOL CMainFrame::OnGestureZoom(CPoint ptCenter, long lDelta)
{
 if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == 
 GF_BEGIN)
 {
  m_dblZoomRatioStart = lDelta;
  m_drawingObject = GetObjectUnderPoint(ptCenter);
}
else if (lDelta != 0)
{
  double zoomFactor = (double)(m_dblZoomRatioStart + lDelta)/ 
   m_dblZoomRatioStart;		
  m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);	  
  m_dblZoomRatioStart += lDelta;
  RedrawWindow();
 }

 return TRUE;
}

Some gestures have legacy behavior when not handled by the developer. For more information, see "Legacy Behavior of Gestures".

Gesture CWnd Virtual Method Legacy Behavior Default
Zoom Virtual BOOL OnGestureZoom(CPoint ptCenter, Long lDelta); Yes: Ctrl + Scroll Wheel Enabled
Pan Virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo); Yes: Scroll Enabled
Rotate Virtual BOOL OnGestureRotate(CPoint ptCenter, Double dblAngle); No Off
Two-Finger Tap Virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter); No Enabled
Press and Tap Virtual BOOL OnGesturePressAndTap(CPoint ptPress, Long lDelta); No Enabled

Legacy Behavior of Gestures

By default, rotate gesture detection isn't enabled. If your application wants to detect and handle this gesture, or control what gesture events to receive in general, call CWnd::SetGestureConfig for each affected window, as in this example:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{	...
  CGestureConfig cfg;
  GetGestureConfig(&cfg); // get gesture defaults
  cfg.EnableRotate(); // enable rotate gesture	
  SetGestureConfig(&cfg); // set configuration
	...
}

You can handle the WM_TOUCH message directly by overriding the virtual method CWnd::OnTouchInput if you want to create more complex interactions inside your application. The OnTouchInput event handler receives individual touch events as they occur. Your application can either handle each touch event separately or combine them to recognize custom gestures.

You need to call Register¬TouchWindow() as part of the initialization code in order to enable a window to receive this event.

OnTouchInput will get called for each touch point that occurs inside the client area of the window:

BOOL CMainFrame::OnTouchInput(CPoint pt, 
 int nInputNumber, int nInputsCount, 
 PTOUCHINPUT pInput)
{
 if( (pInput->dwFlags & TOUCHEVENTF_DOWN) == 
  TOUCHEVENTF_DOWN )
 {
  CString str;
  str.Format(L"Touch input event %d of %d", 
   nInputNumber, nInputsCount);	
  GetDC()->TextOutW(pt.x, pt.y, str);
 }
}

Windows 7 also provides platform support for object manipulation and inertia processing to fine-tune the multitouch experience. Your application can provide a natural touch interaction with the artifacts provided by its UI. The COM-based APIs enable combinations of gestures to occur at the same time on one or multiple objects (for example, zoom while translating at the same time) and rendering of realistic object physics as a response of those gestures (for example, tossing to a corner or shaking an object).

Taskbar Integration
In the first section of the article we talked about two new capabilities of the new Windows Taskbar -- document thumbnail and Jump Lists -- and how you can immediately take advantage of them by simply recompiling your code with MFC 10. The new Windows Taskbar has a few more capabilities you can use to provide the user with visual cues of the state the application is in or quick context menu actions.

Rather than displaying only the most recent documents opened -- the default behav¬ior MFC provides for Jump Lists -- you can customize the Jump List content by using the new MFC CJumpList class. This class provides methods for adding new categories of items, managing the items listed in those categories and adding specific tasks relevant to your application. For example, the following code makes the default category Recent visible, creates a new category called Favorites, adds the currently focused document to it and then appends three tasks that will launch the browser with different URLs:

CJumpList jumpList;

jumpList.InitializeList();
jumpList.AddKnownCategory(KNOWNDESTCATEGORY::KDC_RECENT);
jumpList.AddDestination(L"Favorites", 
 m_pDocument->GetPathName());
	
jumpList.AddTask(strPathToBrowser, 
 L"http://blogs.msdn.com/vcblog/", 
 L"Visual C++ Blog", strPathToBrowser, 0);
jumpList.AddTask(strPathToBrowser, 
 L"http://msdn.com/visualc", 
 L"C++ Developer Center", strPathToBrowser, 0);
jumpList.AddTaskSeparator();
jumpList.AddTask(strPathToBrowser, 
 L"http://msdn.com/windows", 
 L"Windows 7 Developer Center", strPathToBrowser, 0);
	
jumpList.CommitList();

In this example, even after the app exits, the customized Jump List will still be available to users when they right-click on the pinned Taskbar button. The Windows Taskbar takes care of storing the Jump List information for each running or pinned app (see Figure 3).


[Click on image for larger view.]
Figure 3. The customized Jump List.

If your application main window is in the middle of a lengthy task that doesn't require user interaction, it can notify the user of its progress directly by displaying a progress bar in the Taskbar. To display a progress bar in the Windows Taskbar, all you need to do is define the range and set the position by calling CFrameWnd::Set¬ProgressBarRange and CFrameWnd::SetProgressBarPosition. You can also alter the state of the progress bar. For example, in case you can't determine the duration of the task, you can set the progress bar state to be "indeterminate." If the user pauses the task execution or if an error occurs, you can change the state to paused or error, respectively, as in the following example:

CMainFrame* mainFrm = 
 dynamic_cast<CMainFrame*>(AfxGetApp()
 ->GetMainWnd());
if(mainFrm != nullptr)
{
 mainFrm->SetProgressBarRange(0, 100);
 mainFrm->SetProgressBarPosition(40);

 mainFrm->SetProgressBarState(TBPF_ERROR);
}

To make your application communicate a more customized state to the user, take advantage of the Windows Taskbar ability to display overlay icons on top of an application Taskbar button. Do this with the method call CFrameWnd::SetTaskbarOverlayIcon:

CMainFrame* mainFrm = 
 dynamic_cast<CMainFrame*>(AfxGetApp()
 ->GetMainWnd());
if(mainFrm != nullptr)
{
 if(NewDocumentsPendingReview())
  mainFrm->SetTaskbarOverlayIcon(IDR_FILEICON, 
   L"New documents available");
 else
  mainFrm->SetTaskbarOverlayIcon((HICON)nullptr, L""); 
   // clears the overlay icon		
}

Preview, Thumbnail and Search
If your MFC application manages its own document types, then beyond the icon you've selected, Windows Explorer doesn't present much information about the file content. By contrast, Word documents and images offer a thumbnail or preview of the content.

These are not new Windows 7 features; they were introduced in Windows Vista. What is new is that Visual C++ 2010 comes with a set of ATL and MFC classes and wizards that allow you to create Windows Shell Extensions that can leverage the code you wrote for your MFC document and view classes to provide the following:

  • Thumbnail icon generation
  • Document preview
  • Search integration

When creating a new MFC app, the MFC Project Wizard can create a separate ATL project for implementing the Shell Extensions.

When you have an existing application, invoke the ATL Wizard and specify a file extension and the types of handlers you want the project to implement for that: preview, thumbnail and search. If you want the handlers to automatically instantiate your existing MFC document and view classes, make sure you uncheck Generate ATL-based document and provide the name and the full path of the document and view classes, then implement your handler code in the MFC classes. Alternatively, Generate ATL-based document will create a common ATL wrapper class that all handlers will instantiate. Inside this wrapper, you can control whether you want to delegate the implementation to your MFC code or provide an alternative implementation for some of the handlers.

Accessing Windows 7 APIs Directly
Visual Studio 2010 includes the Windows 7 SDK headers and libraries in the box. The SDK gives you direct access to a wealth of new Windows 7 APIs, in addition to those we've already discussed.

Windows 7 comes with support for high-fidelity graphics that you can incorporate in your applications to provide modern interactions and feedback to the user. Direct2D provides immediate access to accelerated-graphics capabilities via a simplified 2-D API that's based on Direct3D. This is an alternative to GDI and GDI+ and provides superior performance while still having a good interoperability with these technologies. DirectWrite enables high-quality ClearType text rendering and layout. DirectWrite takes advantage of hardware-accelerated text when used with Direct2D, but the API can also be used with other rendering engines. The same is true for the new Animation API -- if it's used with Direct2D or other rendering technologies, the

Animation APIs provide a framework for scheduling and executing animations, including sophisticated conflict resolution when multiple animations attempt to manipulate the same variable.

Connected applications are becoming the norm in modern applications. Windows 7 provides native support to both communicate with and implement SOAP Web services by using the Windows Web Services API, which supports a broad set of the WS-* family of protocols and provides an effective way for developers to access the cloud, corporate services or local Web services.

Coupled with these technologies, Windows provides a new framework for consuming and implementing sensors. This new technology helps provide more environmental awareness to Windows-based applications. A new API developed on top of this platform is the Location API, which can give applications coordinate information on the current location of the device (provided there are GPS or wireless sensors enabled and that the user grants access to the data).

Parallelization Enhancements
A well-parallelized application is always more efficient than a single-threaded application. As we move into the age of many-core CPUs, having your application scale with the number of processors becomes critical -- and having the right tools to help with this effort is essential.

New in Visual C++ 2010, the Concurrency Runtime (ConcRT) provides a C++ library that allows developers to harness the power of multi-core in a productive way. The idioms of ConcRT are:

  • Parallel algorithms (generic algorithms that act on collections of data)
  • Tasks (a mechanism to encapsulate what work you want to accomplish rather than how you want to parallelize it)
  • Agents (using message passing for fine-grained dataflow and pipelining of computationally intensive tasks)

Windows 7 has a new lightweight thread scheduler called User-Mode Scheduling (UMS) that lets high-performance apps (rather than the kernel) schedule, throttle and control the overhead due to blocking system calls. You may not use UMS directly to speed up your app, but by supporting this level of flexibility in managing threads at the user-mode level, you let higher-level concurrency libraries (such as ConcRT) provide an optimized multithreading behavior. ConcRT takes advantage of UMS by defining a SchedulerPolicy with SchedulerKind member set to UmsThreadDefault.

The following snippet uses task parallelism (and C++ lambdas) to asynchronously download multiple files from the Web:

Concurrency::task_group tasks; // a task_group 
 // manages the execution of multiple tasks
Concurrency::concurrent_vector<CThumbnail> 
 thumbnails; // concurrent_vector is the thread 
 // safe equivalent of std::vector
Concurrency::concurrent_vector<CString> fileList;
std::shared_ptr<CWebImageManager>	
 objWebImages = 
 make_shared<CWebImageManager>();
for( int i = 0; 
 i < imageUrlList.Image->ResultsCount; ++i )
{
 // task_group::run will 
 // queue a task for execution. 
 tasks.run( [objWebImages, i, imageUrlList, 
  &fileList, &thumbnails, this] { 
  // C++0x lambda expression
 // these lengthy operations will now execute 
 // on a different thread
 CString thumbFileName = objWebImages->
  DownloadThumbnailImage(
  imageUrlList.Image->Results[i]);
 CString imageFileName = objWebImages->  
  DownloadImage(
  imageUrlList.Image->Results[i]);
 // push_back may be called concurrently from 
 // different threads at the same time
 fileList.push_back(imageFileName);
 thumbnails.push_back(CThumbnail::Load(
  imageFileName, thumbFileName));
 } );
}
tasks.wait(); // first blocking call. this will wait 
 // for all queued tasks to complete

Overall, Windows 7 is an exciting release for developers. With thousands of new Windows APIs made available to native coders, Windows 7 provides an enhanced experience for desktop applications.

Visual Studio 2010 is the IDE of choice to take advantage of the Windows 7 platform. With enhancements in MFC and the ATL and the addition of new IDE Designers and Wizards, Visual C++ 2010 gives you the opportunity to be on the cutting edge in terms of leveraging OS functionality.

comments powered by Disqus

Featured

Subscribe on YouTube