From the June 2001 issue of MSDN Magazine.

MSDN Magazine

Pocket PC: Seamless App Integration with Your Desktop using ActiveSync 3.1

Dino Esposito
This article assumes you�re familiar with C++, Win32, Windows CE
Level of Difficulty     1   2   3 
Download the code for this article: PPC.exe (242KB)
Browse the code for this article at Code Center: PocketPC-ActiveX
SUMMARY ActiveSync 3.1, AvantGo channels, and Internet Explorer 5.0 Mobile Links allow you to provide content for Pocket PC users over the Internet or company intranet. This article explains how to take advantage of ActiveSync and AvantGo functionality as well as how to extend ActiveSync's data synchronization capabilities by writing a custom service provider for Windows CE.
      The second part of the article uses eMbedded Visual C++ to develop ActiveX controls for the Pocket PC that work on both the desktop PC and on the Pocket PC platforms. Customizing the Pocket PC's Today screen using a custom Today item is demonstrated.

Pocket PCs are becoming significant players in the mobile computing arena, competing with other palm-sized devices, WAP cell phones, and laptops in terms of usability, price, and programming power. And for developers programming for Windows®, the software model is already familiar. If you've been following the news on Pocket PCs, you know that Microsoft provides Windows-based development environments and tools to allow you to create applications for the Pocket PC as well as other embedded systems. The latest of these tools is Microsoft® eMbedded Visual Tools, a shrink-wrapped, standalone, localized version of Visual Basic® and Visual C++®, designed for Windows CE 3.0. For an overview of the Pocket PC platform and tools, refer to the January 2001 issue of MSDN® Magazine, in which Paul Yao and Joshua Trupin covered system features, programming tools, and porting of existing applications in two articles.
      In this article, I'll talk about data synchronization between the desktop and the Pocket PC, and how Microsoft ActiveSync® 3.1 provides the necessary capabilities. Because the Pocket PC has seamless synchronization with the desktop, you may want to become a content provider for AvantGo (https://www.avantgo.com) or for an intranet information system. I'll tell you how to do so. You may also want to write your own service provider to extend the data synchronization capabilities of ActiveSync 3.1.

Figure 1 The Pocket PC Today Screen
Figure 1 The Pocket PC Today Screen

      I'll also talk about customizing the Pocket PC user interface. The designers at Microsoft gave the Pocket PC compatibility with other Windows operating systems and required that the behavior of applications be consistent. As you will note, there is just one way of doing most tasks on the Pocket PC. The Help files provided with the eMbedded Visual Tools specify UI guidelines and design principles for the limited screen real estate of a Pocket PC. While the Pocket PC allows for customization and user-specific adaptations, they are subject to rules and regulations. In this article I'll explain how to use eMbedded Visual C++ to write ActiveX® controls with code that can be used on both the desktop and the Pocket PC, and how to customize the Today screen (see Figure 1).

Data Synchronization and ActiveSync 3.1

      Data synchronization is nothing new for Windows users and developers. In Windows 95, the My Briefcase tool was a way to keep files on laptops or external storage devices synchronized with those on the primary machine. In Windows 2000, the briefcase functionality has been integrated with the shell and is available on each folder.
      While a tool like the original My Briefcase can be useful—especially if content to be synchronized is organized on a folder basis—it is file-centric, not information-centric, so it doesn't address the need to synchronize a wide variety of information.
      Microsoft introduced the Offline Files folder so users can access Web pages even when they aren't connected to the Web. The Offline Web Pages folder is an HTML-centric folder that automatically downloads pages for offline use.
      ActiveSync 3.1 provides synchronization services between the primary desktop PC and the Pocket PC, allowing you to take your personal information wherever you go. This information can be in the form of e-mail messages, contacts, task lists, stock quotes, weather forecasts, even your horoscope. The information comes from a database or a compound file as a set of records, not from single files.
      Not only does ActiveSync pass personal information between desktops and Pocket PCs, it also allows you to develop code and automatically run it on a connected device. ActiveSync 3.1 eliminates the need to install Dial-up Networking on any supported version of Windows. It also eliminates the need to enable remote access on Windows NT® and Windows 2000 operating systems. ActiveSync can work over a serial port as well as through USB, infrared, dial-up, and Ethernet connections, and it has the ability to automatically detect and configure the port being used for the PC-to-device communication.
      With ActiveSync 3.1, the connection speed has increased from 19.2Kbps (available with previous versions) to a more reasonable 115KBps. A more detailed technical overview of ActiveSync 3.1 is available at https://www.microsoft.com/mobile/pocketpc/downloads/activesync.asp, where you can also download the product.

Figure 2 The ActiveSync 3.1 UI
Figure 2 The ActiveSync 3.1 UI

      After you install ActiveSync 3.1, you can specify the information to be synchronized between the device and the desktop PC. Figure 2 shows the ActiveSync desktop module in action. From the desktop module, you can select the types of information you want to share between the Pocket PC and the desktop PC and also specify how ActiveSync should resolve conflicts. ActiveSync can synchronize e-mail, contacts, appointments, and notes with Microsoft Outlook® 2000 and Outlook 2002. Outlook Notes on your desktop computer are the equivalent of Pocket PC's Notes program. Both applications support text, handwriting, and voice. Figure 3 shows an Outlook note on the desktop and on the Pocket PC.

Figure 3 Synchronized Outlook Notes
Figure 3 Synchronized Outlook Notes

      ActiveSync also provides a mobile device desktop explorer that can be accessed by selecting the Explore button on the ActiveSync user interface (as you saw in Figure 2). Files and folders viewed from this explorer mirror the content of the Pocket PC Object Store (see Figure 4).

Figure 4 Pocket PC Explorer and Mobile Explorer
Figure 4 Pocket PC Explorer and Mobile Explorer

The content is provided by a shell namespace extension which is displayed as a node in the explorer's left pane. You can use this to rename or delete files and folders using the Windows user interface rather than the Pocket PC tools. You cannot directly open files on the device from the Explorer view, however. You need to bring the files onto the desktop PC. When you do this, using drag and drop, for example, a dialog like the one in Figure 5 appears to indicate that a conversion is in progress. This conversion is handled by ActiveSync.

Figure 5 Copying from Pocket PC to Desktop PC
Figure 5 Copying from Pocket PC to Desktop PC

Synchronizing with AvantGo

      AvantGo enables you to browse your favorite Web sites on your mobile device and to download your choice of hundreds of content channels that have been optimized for the device's small screen. ActiveSync 3.1 tightly integrates with AvantGo. You can register to consume content from AvantGo, but you can also register to become a content provider and share your information with thousands of other users.
      Let's take a quick look at how easy it is to access and read content from AvantGo. You just sign up with the service and register for the channels you like. You can receive news, stock quotes, flight schedules, maps, weather, and much more. The sign-up process downloads the AvantGo Integrated Client (about 2MB) to your desktop and asks you to run ActiveSync. Figure 6 shows one step of this process. Just follow the instructions and you have all the selected channels ready to download on your device any time you re-sync with your primary PC and are connected to the Internet. If you don't have a full-time Internet connection, you will be prompted to connect.

Figure 6 Synchronization with AvantGo
Figure 6 Synchronization with AvantGo

      Once the content has been successfully synchronized, you can access it through Pocket Internet Explorer. The mobile links are stored in the Favorites folder. Figure 7 shows The Weather Channel's weather.com site. To use it, you enter the name of a city, and it returns the weather forecast for that city. The page updates when you re-sync your Pocket PC.

Figure 7 AvantGo Channels
Figure 7 AvantGo Channels

      After you install ActiveSync 3.1, you will notice that Internet Explorer 5.0 (or higher) on your desktop PC has a new Tools menu item: Create Mobile Favorite. When you click this menu item while visiting an online site, you can add the current URL to the Favorites folder of Internet Explorer on your Pocket PC device. This operation occurs immediately if the device is connected; otherwise it will take place the next time you synchronize.

Becoming an AvantGo Content Provider

      In addition to consuming AvantGo content, you can easily sign up to become an AvantGo content provider. A simple Web wizard guides you through the creation of an AvantGo channel, which you create using HTML and your favorite Web development tools. These pages need to stay on a publicly available Web site without a corporate firewall. AvantGo acts like a proxy server between you, your data, and your users. Figure 8 shows how AvantGo channels bring content to your Pocket PC device.
      The AvantGo server downloads all the requested pages and performs some preprocessing on them. For example, it shrinks images that are too large and removes unsupported HTML elements. It also makes sure that the overall size of the pages is less than the maximum channel size. Normally, this size doesn't exceed 100KB, but the maximum channel size is one of the parameters you can specify when creating the channel.
      The AvantGo server also controls the link depth—how deep it should go from the initial Web URL location to retrieve related pages. Depth 1 means it has to download only the pages directly linked from the home page. Depth 2 includes the pages pointed to from those found at level 1, and so on. Any link that goes beyond the fixed depth level is not retrieved.
      AvantGo currently does not support certain HTML features including frames, layers, CSS, animated GIFs, rollover images, Dynamic HTML, and image maps. It also doesn't support the ability to specify fonts or the italic style for fonts. It can't handle dynamic resizing for images and it doesn't support Java-language applets, plug-ins, or JavaScript cookies. If you do include any of these elements, AvantGo just ignores them.
      Because AvantGo is a Web-wide application that offers a service to you, it can become a key part of your system architecture. With AvantGo, you only have to worry about the content.

      If you have the need to push your content to handheld-device users, AvantGo is a must-have Web service. However, if you only need to attract a smaller category of users or to provide offline, device-specific content to the users of a corporate network, then Internet Explorer mobile links might be a better approach.

Figure 9 HTML Formatting Versus Unsupported DHTML
Figure 9 HTML Formatting Versus Unsupported DHTML

      Functionally speaking, AvantGo channels and mobile links are roughly the same. They both allow you to expose synchronized information as HTML pages to Pocket PC users. AvantGo, however, preprocesses HTML pages to make sure they fit that category of device. Internet Explorer doesn't, but it allows you to publish information on intranets within a corporate firewall. Basically, the Internet Explorer extension installed by ActiveSync 3.1 acts as a simpler AvantGo server without the explicit notion of a channel, without special preprocessing, and without restricting you to download only Web-accessible resources. Figure 9 shows the home page of my ASP.NET-based intranet, which I have not optimized for the Pocket PC. Keep in mind that advanced DHTML features such as element behaviors aren't supported yet. The right side of Figure 9 shows what could happen if you try to use such features. A practical rule is to always test your pages on as many downlevel browsers as possible.

Data-specific Synchronization with Service Providers

      So far I've described the features of ActiveSync 3.1 without explaining its architecture and how you could extend its capabilities. ActiveSync is based on a plain client/server architecture. It consists of a service manager and a service provider. The service manager, built into Windows CE Services, coordinates the synchronization process between the desktop and the device. It provides connectivity, detects changes, resolves conflicts, and transfers data. Synchronization tasks specific to the data must be accomplished by special modules called service providers.
      A service provider has two modules—one on the desktop and one on the client device. Both modules are implemented as in-process COM objects. The interfaces they expose allow the service manager and the other service provider module to request the tasks that the service provider has been designed for and make the module's specific capabilities publicly known. The overall model looks like the shell namespace extension model in which Explorer plays the role of the service manager. All registered namespace extensions are service providers, each offering specific capabilities through the same programming interface. Each item listed in the Details dialog box in ActiveSync (see Figure 2) has a service provider module behind it that talks to the service manager.
      Writing a new minimal service provider is easy; just write a COM object implementing two COM interfaces, IReplStore and IReplObjHandler. IReplStore manages the state of all the objects found in a generic data store. The data store can be a database, a compound file, a proprietary file, a subtree of folders, or any other information source you want to synchronize. For example, when it comes to synchronizing notes, the ActiveSync built-in provider manages the Outlook folder where note files are stored.
      The IReplStore interface detects changes in an object and displays a user interface so that the user can set options and resolve conflicts. IReplObjHandler is a more specific interface for serializing data and deleting or renaming objects.
      As stated earlier, an ActiveSync service provider is a two-part component, one of which must reside on the device. The device counterpart must implement the same IReplObjHandler interface. In addition, it needs to export three callback functions: InitObjType, GetObjTypeInfo, and ObjectNotify. A fourth function, ReportStatus, is optional.
      Both modules of an ActiveSync service provider must be registered as COM components on their respective machines. In addition, on the desktop you need to register the progID of any object type that the COM components handle along with the display name to be used.
      In the device's registry you should enter information about the synchronizable objects available. The registry path is:

  HKEY_LOCAL_MACHINE
  
\Windows CE Services
\Synchronization
\Objects

 

Under the Objects node, create a new node with the name of the object you want to register. Add entries called Store and Display Name, which will contain the DLL name and the display name of the object (see Figure 10).

Figure 10 Synchronizable Objects in Pocket PC Registry
Figure 10 Synchronizable Objects in Pocket PC Registry

More information and code examples showing how to create ActiveSync service providers can be found on MSDN in the Windows CE documentation at https://msdn.microsoft.com/library/wcedoc/wceintro/cestart.htm.

Developing ActiveX Controls for the Pocket PC

      Now let's look at customizing the Pocket PC through shell programming and application development. First, I'll discuss ActiveX controls for the Pocket PC, and later I'll explain how you can customize the Today screen of your Pocket PC.
      As the Pocket PC is based on Windows CE 3.0, a significant part of the Win32® API is available for development. A key difference from the full Win32 API is the user interface. For example, Pocket PC applications can't use popup windows and they have to be implemented with a single-document interface. The good news is that you can reuse Pocket PC code through DLLs, COM Automation objects, and ActiveX controls. I'll talk more about code reuse later in this article.
      Writing ActiveX controls for the Pocket PC can be somewhat tricky, but this has nothing to do with the slightly different Win32 API that Windows CE supports. The real challenge lies in the way in which you write, compile, and test ActiveX controls to be used on a connected handheld device.
      To help you understand the development process, let's first investigate how to start a new application for your Pocket PC device. For starters, create a new MFC project with eMbedded Visual C++, making sure that you select the right target hardware platform. For my Compaq iPAQ, the CPU is an ARM and the target configuration is Win32 (WCE ARM). If you just want to run the code using the desktop emulator, choose Win32 (WCE x86em) instead. If you choose to target more platforms with the same code base—typically, the default device and the desktop emulator—the combobox with the target device will update as you select the project configuration. To add new devices, use the Configure Platform Manager dialog from the Tools menu.
      Once the code has been written and compiled successfully, it is downloaded to the target device and run. All this happens in an extremely smooth manner regardless of the device platform. Just make sure you have ActiveSync (or another connection layer) properly installed and working.
      The development process starts getting a little more complicated if you want to use custom ActiveX controls. To write ActiveX controls for the Pocket PC, you definitely need Visual Studio®. To create a custom control, right-click on the dialog in the resource editor and select Insert ActiveX Control. When you select the control DLL, you inevitably run into an error message. What's going on? eMbedded Visual C++ needs to find a registered copy of the control in the desktop registry. In other words, when it comes to developing ActiveX controls for the Pocket PC, you always need to set up two versions: one for the desktop PC and one for the device. This feature has very little to do with the device itself, but is more a requirement of eMbedded Visual C++. Incidentally, this also happens with previous versions of tools for Windows CE-based development—specifically the Windows CE Toolkit for Visual C++. Steve Zimmerman pointed this out in his article "The Tools You'll Need to Build Embedded Apps: Windows CE Toolkit for Visual C++ 6.0" that appeared in the July 1999 issue of MSJ. Let's see how to work around this problem.
      Ideally, these two different projects will share the same source files. Follow these steps for one possible way to do this.

  1. Temporarily rename the folder with the Pocket PC ActiveX control. For example, if your control is in a folder called PocketMSDN, rename it PocketMSDN1.
  2. Launch Visual C++ 6.0 and create an ActiveX control project using the same name. Now you have a new PocketMSDN folder with a .dsp and a .dsw file in the Visual C++ workspace. Make sure the Visual C++ project contains exactly the same files as the Pocket PC project.
  3. Rename the DSP and DSW files and copy them to the PocketMSDN1 folder. Let's say that you rename them PocketMSDN_Win32.dsp and PocketMSDN_Win32.dsw.
  4. Once you've moved those files to the Pocket PC project folder, delete the Win32 project and restore the PocketMSDN1 folder to its original name. In the PocketMSDN folder you should have two pairs of DSP/DSW files—one for the desktop and one for the Pocket PC project—and, more importantly, they're sharing the same source files.
  5. Modify the desktop version of the DSW file to make it point to its DSP counterpart. For example, edit the DSW file in Notepad and change the following line
    Project: "PocketMSDN"=".\PocketMSDN.dsp"
    to
    Project: "PocketMSDN"=".\PocketMSDN_Win32.dsp"
  6. You also need to change the name of the output file. It defaults to the name of the DSP file, but you should change it to the name of the Pocket PC project. There's a subtle reason for this, which I'll explain in the next section.

      Now you're ready to compile the desktop version of the ActiveX control. Once this has been done, the existing ActiveX control can be used for the Pocket PC. Right-click on the dialog in the Windows CE Resource Editor, click on Insert ActiveX Control, and select the desktop DLL.
      The sample control is a textbox that lets you select file names. It has a companion button you can click to browse for files. The name of any file you select is then automatically displayed in the textbox.

Same Source, Two Platforms

      The code in Figure 11 shows the most interesting part of the ActiveX control source code. It is built as an ATL composite control, a control architected in much the same way as controls in Visual Basic. The client area of an ATL composite control is based on a dialog box. The way it works is determined by the combination of controls placed on the form. In the sample code, I used an edit control and a button connected with some message handlers.
      The control manages automatic resizing with the WM_SIZE message and knows how to initialize the WM_INITDIALOG handler. It also handles a couple of dialog-wide messages such as BN_CLICKED and EN_CHANGE. These messages indicate that the button has been clicked and the text in the edit control has changed. When you click on the Browse button in the edit control, the Open File common dialog appears. This dialog has a number of features under Windows CE, Windows 9x, and Windows 2000, including hooking, custom templates, and a places bar. These aren't features you can expect on a Pocket PC. However, the same minimal code works unchanged on both platforms.

  ZeroMemory(&ofn, sizeof(OPENFILENAME));
  
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.lpstrFile = szFile;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = OLE2T(m_strPath);
ofn.hwndOwner = ::GetParent(m_hwndEdit);
GetOpenFileName(&ofn);

 

The owner window and the initial directory, for example, are ignored on a Windows CE system, but work perfectly on a Win32 desktop platform.
      Unless the control you're writing needs to run platform-specific code, you can have two controls for the price of one. If you followed the steps to create the ActiveX control, you're close to having a sort of dual-code ActiveX control that can be compiled on Win32 as well as the Pocket PC platform.

Figure 12 Same Code, Different Platforms
Figure 12 Same Code, Different Platforms

      Figure 12 shows two applications on different platforms that make use of two physically different DLLs, but with exactly the same source code. Also, the project files are in the same folder and you just have to open the proper workspace (a .dsw file) to compile or edit one of them.
      There might be circumstances in which you need to branch your code to comply with specific features of Windows CE or Windows 2000. The GetVersionEx API function works on both platforms:

  BOOL GetVersionEx(
  
LPOSVERSIONINFO lpVersionInformation
);

 

      The field dwPlatformId of the OSVERSIONINFO structure returns a value indicating the Windows platform on which your code is running. Although the constant for Windows CE is defined only in the headers file for Windows CE, it's easy to find out that the value is 3. The following code snippet shows how to branch your code based on runtime conditions:

  OSVERSIONINFO osvi; 
  
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwPlatformId == 3)
// is Windows CE
else
// another Windows platform
// 1-Windows 9x, 2-Windows NT or Windows 2000

 

      You could also resort to compile-time pre-processor directives to distinguish the code to run under Windows CE from the version for Windows 2000 or other Windows platforms.
      If you need to know the major and minor version numbers of the underlying operating system, you can safely use the dwMajorVersion and dwMinorVersion fields of the OSVERSIONINFO structure, regardless of the platform you target.
      When you write code destined to run on both Windows CE and Windows 2000, you should also pay attention to the constant strings you use. Windows CE 3.0 supports only Unicode strings. However, using a common-use macro such as _T and the TCHAR type should make the code extremely portable.
      As a final note on Pocket PC ActiveX controls, remember that you can only write them with eMbedded Visual C++, choosing between the MFC ControlWizard and the ATL COM AppWizard as the underlying framework. eMbedded Visual Basic can create Windows CE-targeted applications, but not COM components.

Customizing the Today Screen

      The Today screen is the Pocket PC counterpart of the Active Desktop. It features a list of shortcuts for applications you can access. This list is modifiable to some extent, as you saw in Figure 1. By default it shows a number of predefined applications, and you can check or uncheck each of the listed programs and set their options. Extending this list with new applications is a bit more complicated, but it's exactly what you want to do to make your application easily accessible. Let's see how to write a plug-in module to monitor useful links to Microsoft .NET Web sites.
      An item created for the Today screen is a plain old Win32 window. You must register a new window class for it, take care of its window procedure, and handle at least the WM_PAINT and WM_LBUTTONUP messages. The Today screen contains some default items that you can hide if you prefer.
      All the items for the Today screen are listed under the following key in the device's registry:

  HKEY_LOCAL_MACHINE\Software\Microsoft\Today\Items
  

 

      For a custom item, you just create a new node and enter a particular value in the Type field of the node. However, I'll talk more about the registration procedure later on, once I cover some programmatic aspects of Today items.
      A plug-in for the Today screen is a Windows CE DLL that needs to export a couple of functions, one of which is optional, depending on whether you want to enable the Options dialog. The function you always implement is InitializeCustomItem. You must export it in your library DEF file with a precise number (240). Assuming the library is called QuickLinks, the following is valid content for the project DEF file:

  LIBRARY    QuickLinks
  

EXPORTS
InitializeCustomItem @240 NONAME

 

      When writing this type of Windows CE DLL, you must use the old-style DEF file to export public functions because only the DEF interface allows you to export functions by number. In this case, you also must qualify each function as NONAME. The optional NONAME attribute means that you want to export it by ordinal only. As you may have guessed, the Pocket PC system will then attempt to load the function only by number, never by name, and it requires that number be 240. If you export InitializeCustomItem with another number or by name, then your plug-in simply won't work. Aside from this little peculiarity, InitializeCustomItem is quite straightforward.
      The prototype of InitializeCustomItem looks like this:

  HWND APIENTRY InitializeCustomItem(
  
TODAYLISTITEM *ptli,
HWND hwndParent);

 

      The TODAYLISTITEM structure provides general information about the item. This information includes the registry key from which the system is reading about the item, the DLL which implements it, and other settings such as the presence of an Options dialog or the visibility status on the device's Today screen. A Today item, in fact, could be installed but disabled by the user. The TODAYLISTITEM structure should be considered read-only. If you think you need it, feel free to use the information contained, but never attempt to change it.

The Today Item's Window

      Creating a new Today item is as easy as creating a window of a new class using the familiar Win32 programming style.

  hwndTodayItem = CreateWindow(
  
TODAY_CLASS,
TODAY_TITLE,
WS_VISIBLE|WS_CHILD,
CW_USEDEFAULT,
CW_USEDEFAULT,
240, 0,
hwndOwner,
NULL,
g_hThisDll,
NULL);

 

The window must have the WS_CHILD style and must be a child of the hwndOwner—one of the arguments that InitializeCustomItem receives. The function must return the handle of the freshly created window. You can also specify some other window styles such as WS_BORDER. Keep in mind, though, that this would violate the consistency of the overall user interface.
      The documentation recommends that you give the window the maximum size available, usually 240 pixels. It also suggests that you assign a pixel height of zero as the window will be automatically resized by the system. I'll have more to say about this toward the end of this article.
      The window procedure must supply a user interface and draw the window's client area. To do this, you handle the WM_PAINT message—but try to minimize the activity as much as possible, as this message gets invoked quite often. There's no particular guideline on how to draw the client area. However, you might want to mimic the structure of the other predefined items. They all have a little bitmap on the left side followed by one or more lines of text aligned on a second column. All items have a 1-pixel separator.
      One way to reproduce the interface of the Today items is to use a 16x16 icon followed by text. The text has a 30-pixel offset from the screen's left boundary. You can also use a larger bitmap in lieu of the small icon. This code is one example:

  hIcon = (HICON) LoadImage(g_hThisDll, 
  
MAKEINTRESOURCE(IDI_QUICKLINKS),
IMAGE_ICON, 16, 16, 0);
BeginPaint(hwnd, &ps);
DrawIcon(ps.hdc, 0, 0, hIcon);
CopyRect(&rt, &ps.rcPaint);
rt.left += 30;
DrawText(ps.hdc, TODAY_TITLE,
lstrlen(TODAY_TITLE), &rt,
DT_SINGLELINE | DT_VCENTER);
EndPaint(hwnd, &ps);

 

      If, like me, you spent the best years of your youth fighting with the Windows SDK and the examples in Charles Petzold's book, this code should seem very familiar. For the rest of you, I'll say that BeginPaint and EndPaint wrap the core paint activity of the window. The PAINTSTRUCT structure filled by BeginPaint gives you the portion of the window's client area to repaint and the device context to use. The icon is loaded from the library's resources and drawn with the DrawIcon API function. DrawText takes care of the text. It utilizes default settings for the text color, the background brush, and the font. The text is made to fit into the specified rectangle according to the given flags. Don't forget to specify the exact text length if you want to avoid weird trailing characters.
      Handling the WM_LBUTTONUP message is the only way you can do something in response to the user clicking on the item. In this Quick .NET Links sample, I just launch Internet Explorer for the Pocket PC and have it point to a quicklinks.htm page that looks like this.

  <html><body>
  
<h2>Quick <b>.NET</b> Top Links</h2><hr>
<ul>
<li><a ...>Wintellect</a></li>
<li><a ...>VB2TheMax</a></li>
<li><a ...>ASP.NET</a></li>
<li><a ...>ASP.NET to the Max</a></li>
<li><a ...>ADO.NET to the Max</a></li>
</ul>
<hr>
</body></html>

 

      ShellExecuteEx is the function that does most of the work for you, as you can see here:

  SHELLEXECUTEINFO sei;
  
ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.lpFile = _T("iexplore.exe");
sei.lpParameters = HTML_QUICKLINKS;
sei.nShow = SW_SHOW;
ShellExecuteEx(&sei);

 

      Figure 13 shows this new Quick .NET Links Today item on my (Italian) Compaq iPAQ Pocket PC. Displayed this way, the Quick .NET Links link isn't very useful. However, as soon as I create Internet Explorer mobile links to these URLs and synchronize my device, information will be available. Of course, an AvantGo channel would be another way of exposing the same service.

Figure 13 Today Page in Italian
Figure 13 Today Page in Italian

Custom Messages

      The Today item must be aware of a couple of other messages: WM_TODAYCUSTOM_CLEARCACHE and WM_TODAYCUSTOM_QUERYREFRESHCACHE.
      With WM_TODAYCUSTOM_CLEARCACHE you can free any item-specific data that has been cached. This message reaches the DLL when the user closes the Today Settings page. The return value of this message is ignored.
      WM_TODAYCUSTOM_QUERYREFRESHCACHE queries the item to find out if it is time to refresh cached data. In this case, returning TRUE is interpreted as if something important happened. The Pocket PC will then send a WM_PAINT message to the item to give it the opportunity to reflect changes in its user interface. This message is sent periodically. If you return FALSE, you won't get any flickering due to the relentless repaint caused by WM_PAINTs being sent. Of course, if you just need to refresh data every now and then, implement a mechanism to return TRUE only when really necessary. This message has some implications for the way in which the item gets resized, but I'll cover this after explaining how to register a custom Today item.

Registering Today Items

      As I mentioned earlier, you need to register a Today item in the device registry. You can access this registry through the Windows CE Remote Registry tool available from the eMbedded Visual C++ IDE. This tool, shown in Figure 14, allows you to work on the PC emulation registry and the Pocket PC device at the same time. Once you've created a new key under

  HKEY_LOCAL_MACHINE\Software\Microsoft\Today\Items
  

 

add a few entries to complete the registration. The most important is a REG_SZ entry named DLL that contains the device path of the Today item's DLL. Normally, you put this DLL in the device's Windows folder.

Figure 14 Windows CE Remote Registry Editor
Figure 14 Windows CE Remote Registry Editor

      Another important entry is a REG_DWORD entry Type, which must be set to a value of 4 to indicate that it is a non-system item. There is also Enabled, a REG_DWORD element that you can set to either to 1 or 0, depending on whether or not you want the item to appear on the screen. The Options entry is another DWORD value. It enables or disables the Options button on the Today's preference dialog box. When the user clicks on Options, the Pocket PC attempts to load another specific function from the DLL. (More on this in the next section.) The Order value determines the ordinal position at which the item will appear. By default, it displays as the last item at the bottom of the screen. All this information is programmatically available for the library through the TODAYLISTITEM argument of InitializeCustomItem.

The Options Dialog

      If your item needs an Options dialog, start by exporting the following function:

  CustomItemOptionsDlgProc @241 NONAME
  

 

      Once again, the export number is fundamental for the feature to work. The function is just the window procedure for the Options dialog box. A minimal version may look like this:

  switch (msg) {
  
case WM_INITDIALOG:
return TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK) {
EndDialog(hDlg, TRUE);
return TRUE;
}
break;
}
return 0;

 

      OK, but how do you set the template of the dialog? There's no place where you can specify which template you want to use. The API assumes that you're using a template ID called IDD_TODAY_CUSTOM that is defined like this:

  #define IDD_TODAY_CUSTOM    500
  

 

      The constant is defined in the todaycmn.h header file; you should include the file wherever needed. A Pocket PC dialog needs some extra work, however, if it is to become totally consistent with the rest of the system dialogs. In particular, you should place the OK button on the system caption bar and resize the dialog to make it span the entire available screen area. To do this, use the SHInitDialog function that is exported by aygshell.lib and defined in aygshell.h. Of course, you must link the aygshell.lib and include the aygshell.h in your project. Once this has been done, you must then insert the following code in the handler for the dialog's WM_INITDIALOG message:

  SHINITDLGINFO shidi;
  
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLG;
shidi.hDlg = hDlg;
SHInitDialog(&shidi);

 

      The two flags force the Pocket PC system code to display the typical OK button and enlarge the dialog's client area to occupy the whole screen (see Figure 15). Next, use Win32 code to configure the custom controls you placed in the template and make them interact. Don't forget to close the dialog using EndDialog.

Figure 15 The Options Dialog
Figure 15 The Options Dialog

Window Height and Resizing

      As I mentioned earlier, the eMbedded Visual C++ documentation recommends that you assign a height of zero pixels to the item's window while creating it. The documentation, though, fails to emphasize that this is only half the task. If you simply set a height of zero pixels, what you get is a window of null height, making it invisible. The documentation correctly states that the item will be automatically resized, but this happens only if you handle the WM_TODAYCUSTOM_QUERYREFRESHCACHE message and explicitly remind the system about the current height of the item, like this:

  case WM_TODAYCUSTOM_QUERYREFRESHCACHE:
  
ptli = (TODAYLISTITEM *) wParam;
ptli->cyp = 25;
return FALSE;

 

      By setting the ptli->cyp field, you inform the system about the dynamic height of the window. Without this little piece of code there's no way for the system to know the actual height of the item. Consequently, it remains set to zero as specified in the call to CreateWindow.
      When I first started playing with the Today API, I assigned a non-null height to the item's window. It worked fine, and I came to the conclusion that the documentation was uselessly picky. Then I tried to move up and down the Quick .NET Links item using the settings dialog box (the dialog accessed from selecting Settings on the Start menu on a Pocket PC). Surprisingly, the system was unable to handle this properly. The item was correctly displayed only when in its default position—at the end of the items list. In all other cases, serious repaint problems made the Today page of my Pocket PC rather unusable. This happened both with the Compaq iPAQ and the PC emulation device.
      Then I changed the code to make it comply with the documentation, reading between the lines that handling WM_TODAYCUSTOM_QUERYREFRESHCACHE is a must. It worked great under PC emulation and I could display the custom item in any position. When I downloaded the code on my iPAQ, though, it didn't work. The item was always of height zero and then invisible, but at least I didn't run into any repaint problem!
      The definitive solution to this is to remember to unregister the Today item's window class from the system. In other words, check to see if the window class exists, and if it does, unregister it before you create it.
      You can learn an important lesson from my experience. It's definitely true that debugging a Pocket PC application is much easier if you use the PC emulation device. However, the real device doesn't always behave as expected, and you should be aware of this.
      To conclude this discussion, let me share a couple of practical tips on setting up testing environments for Today extensions.
      Today items are to the Pocket PC what shell extensions are to Windows Explorer. You never know how or when they get unloaded from memory. Invariably, you get a copy error when eMbedded Visual C++ attempts to overwrite the existing DLL. If you use the PC emulation device for testing, shut down the Pocket PC emulator and then manually copy the executable from your project directory to the Windows CE local folder. If you cannot terminate the emulator through its user interface, then kill it through the Task Manager. The name of the emulator is shell32.exe. The default Windows CE system folder in emulation mode is:

  C:\Windows CE Tools\wce300\MS Pocket PC\emulation\palm300\windows
  

 

      After you copy the DLL, launch the emulator again from the Start menu.
      It's a bit more complicated to kill the Today extension if it is already running on a real device. The rub is that all Today items get loaded as soon as you turn the device on. More importantly, they always get loaded, even if they've been disabled through the Settings dialog. To unload the Today extension in this case, follow these steps. First, make the Today item point to another, non-existing DLL. Open the Windows CE Remote Registry Editor, connect to the device, and replace the content of the DLL entry. Next, reset the Pocket PC. Now try to copy the new DLL, either recompiling with eMbedded Visual C++ or through the device folder within Explorer. If it doesn't work, try it again!
      When the Today extension is finally unloaded, restore the original content in the DLL entry of the registry and rerun the Today page. Playing with the iPAQ, I noticed that the Today home page isn't really refreshed until you explicitly open it. This causes the complete reload of the items from memory.

Conclusion

      Real-world programming for the Pocket PC requires a good understanding of how things work under the hood of MFC and ATL in Windows. Performance constraints mandate that you dirty your hands with SDK programming details and lose yourself in the maze of WM_XXX messages. Adapted versions of MFC and ATL are a great help but, in my humble opinion, they don't change a key requirement for real-world Pocket PC programmers: solid expertise in Win32 programming.

For related articles see:
Pocket PC: Migrating a GPS App from the Desktop to eMbedded Visual Basic 3.0
Windows CE: eMbedded Visual Tools 3.0 Provide a Flexible and Robust Development Environment
For background information see:
https://www.microsoft.com/mobile/developer/technicalarticles/todayapi.asp.
https://avantgo.com/support/developer/
Dino Esposito is a trainer and consultant based in Rome, Italy. Author of several books for Wrox Press, he now spends most of his time teaching classes on ASP.NET and ADO.NET for Wintellect (https://www.wintellect.com). Get in touch with Dino at dinoe@wintellect.com.