MobileNotifications and Power Management under Windows Mobile: Benefit from Both

Notifications and Power Management under Windows Mobile: Benefit from Both

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

One misty moisty morning
When cloudy was the weather…

One day a good friend of mine, David Herman, came to me, claiming that a piece of software that was supposed to take its data automatically from a remote server in the middle of the night, stopped working on Windows Mobile 2003 devices. More exactly, it had started to operate, but could not perform all the required steps. Not that it didn’t work at all, no, but the PDA’s screen was turned off, communications weren’t opened, and so forth. a After quick search on the Web, I found that, to my great surprise, there is nothing that might solve the problem. Well, maybe I did not search hard enough. Anyway, I’ve started to investigate the case, and so this article has begun…

This article isn’t dedicated to all of title’s topics in depth, but rather intends to combine bits from here and from there to get all the stuff working together. There is no need to dive into well-documented functionality, at least for Notifications. Windows CE Power Management articles are also presented in MSDN. It left us just to add one and one. Besides, Windows Mobile 5.0 gives a programmer a few more options for customization, so I will overview them as well.

Two Faces of Notifications

When one talks about Notifications under Windows Mobile, one usually means two main areas: shell notifications (defined in aygshell.h) and user notifications (notify.h respectively). The first type allows your application to inform the user that something important has just happened; for example, that a new message has arrived. The second type is used to deal with a dozen system-level events, such as network connection being established and so forth. This article will discuss both of the above notification types.

User Notifications

I will start from more traditional Windows CE notifications. Those of you who have developed for older versions of Windows CE are definitely familiar with them. You were able to program your application to respond to a set of system events or to be launched at some point in time. But, things have changed since those times, so now you can see the following statement in notify.h:

//
//Obsolete; provided to maintain compatibility only
//
HANDLE CeSetUserNotification (HANDLE hNotification,
                              TCHAR *pwszAppName,
                              SYSTEMTIME *lpTime,
                              PCE_USER_NOTIFICATION
                              lpUserNotification);
BOOL   CeRunAppAtTime (TCHAR *pwszAppName, SYSTEMTIME *lpTime);
BOOL   CeRunAppAtEvent(TCHAR *pwszAppName, LONG lWhichEvent);
BOOL   CeHandleAppNotifications (TCHAR *pwszAppName);

It means that all those old buddies you used to rely on can stop working anytime. Instead of the above functions, the SDK offers you an equivalent means:

BOOL   CeGetUserNotificationPreferences (HWND hWndParent,
                                         PCE_USER_NOTIFICATION
                                         lpNotification);
HANDLE CeSetUserNotificationEx          (HANDLE hNotification,
                                         CE_NOTIFICATION_TRIGGER *pcnt,
                                         CE_USER_NOTIFICATION *pceun);
BOOL   CeClearUserNotification          (HANDLE hNotification);
BOOL   CeGetUserNotification            (HANDLE hNotification,
                                         DWORD cBufferSize,
                                         LPDWORD pcBytesNeeded,
                                         LPBYTE pBuffer);
BOOL   CeGetUserNotificationHandles     (HANDLE *rghNotifications,
                                         DWORD cHandles,
                                         LPDWORD pcHandlesNeeded);

In fact, these are the same APIs but rewritten in other terms. Windows Mobile SDK gives you even more. It defines and uses a CE_NOTIFICATION_TRIGGER struct to declare how to trigger the required entity:

typedef struct UserNotificationTrigger {
   DWORD      dwSize;
   DWORD      dwType;              //dwType Notification type
   //dwEvent - type of event if dwType == CNT_EVENT
   DWORD      dwEvent;
   //lpszApplication - name of application to execute
   TCHAR      *lpszApplication;
   //lpszArguments   - command line (sans app name)
   TCHAR      *lpszArguments;
   //stStartTime - begin of notification period
   SYSTEMTIME stStartTime;
   //stEndTime   - end of notification period
   SYSTEMTIME stEndTime;
} CE_NOTIFICATION_TRIGGER, *PCE_NOTIFICATION_TRIGGER;

In addition to running your application (with the appropriate command line) at a given time or event, now you can specify a time period. Besides, Windows Mobile or CE.NET supports named events, so you may want to use it for lpszApplication instead of the application name in the form of:

".NotificationsNamedEventsEvent Name"

Shell Notifications

This kind of notification is also pretty easy. Quite a few functions handle all the job:

LRESULT SHNotificationAdd(SHNOTIFICATIONDATA *pndAdd);
LRESULT SHNotificationUpdate(DWORD grnumUpdateMask,
                             SHNOTIFICATIONDATA *pndNew);
LRESULT SHNotificationRemove(const CLSID *pclsid, DWORD dwID);
LRESULT SHNotificationGetData(const CLSID *pclsid, DWORD dwID,
                              SHNOTIFICATIONDATA *pndBuffer);

They allow you manipulate the notification’s look-and-feel and features. Windows Mobile 5.0 slightly extends WM 2003’s functionality by adding new members to the SHNOTIFICATIONDATA struct:

typedef struct _SHNOTIFICATIONDATA
{
   DWORD    cbStruct;     // for verification and versioning
   DWORD    dwID;         // identifier for this particular
                          // notification
   SHNP     npPriority;   // priority
   DWORD    csDuration;   // duration of the notification
                          // (usage depends on prior)
   HICON    hicon;        // the icon for the notification
   DWORD    grfFlags;     // flags - see SHNF_ flags below
   CLSID    clsid;        // unique identifier for the
                          // notification class
   HWND     hwndSink;     // window to receive command choices,
                          // dismiss, etc.
   LPCTSTR  pszHTML;      // HTML content for the bubble
   LPCTSTR  pszTitle;     // Optional title for bubble
   LPARAM   lParam;       // User-defined parameter
   // From here, this is WM 5.0 stuff
   union
   {                      // Defines the softkey bar for the
                          // notification
       SOFTKEYMENU skm;   // Either pass an HMENU in skn
                          // (and set SHNF_HASMENU)
                          // or two softkeys in rgskn.
       SOFTKEYNOTIFY rgskn[NOTIF_NUM_SOFTKEYS];
   };
   // Text to put on SK2 on the Today screen. If NULL, will
   // default to "Notification"
   LPCTSTR    pszTodaySK;
   // What to execute when SK2 is pressed. If NULL, the toast
   // will be displayed.
   LPCTSTR    pszTodayExec;

} SHNOTIFICATIONDATA;

The following code sample shows one simple scenario:

void ^157BOOL158^::OnBnClickedButton1()
{
   SHNOTIFICATIONDATA sn  = {0};

   sn.cbStruct = sizeof(sn);
   sn.dwID = 1971;
   sn.npPriority = SHNP_INFORM;
   sn.csDuration = 15;
   sn.hicon = LoadIcon(AfxGetResourceHandle(),
                       MAKEINTRESOURCE(IDR_MAINFRAME));
   sn.clsid = SAMPLE_GUID;
   sn.grfFlags = 0;
   sn.pszTitle = TEXT("Sample Notification");
   sn.pszHTML = L"<html><body><p><form method="POST" action=>"
                L"<p><font color="#0000FF">"
                L"Visit <a href="www.developer.com">
                   <b>Developer.com</b></a> !"
                L"</font></p>"
                L"<p align=right>
                   <input type=button name='cmd:1001' value='OK'>"
                L"<input type=button name='cmd:1002'
                         value='Cancel'></p>"
                L"</body></html>";
   sn.rgskn[0].pszTitle = TEXT("Dismiss");
   sn.rgskn[0].skc.wpCmd = 1003;
   sn.rgskn[0].skc.grfFlags = NOTIF_SOFTKEY_FLAGS_DISABLED;
   sn.rgskn[1].pszTitle = TEXT("Hide Me");
   sn.rgskn[1].skc.wpCmd = 1004;
   sn.rgskn[1].skc.grfFlags = NOTIF_SOFTKEY_FLAGS_HIDE;
   sn.pszTodaySK = L"Run Calc";
   sn.pszTodayExec = L"windowscalc.exe";

   //Add the notification to the tray
   SHNotificationAdd(&sn);
}

Under WM 5.0, you can customize how your notification will appear when displayed for the user:

and on the Today screen:

Shortly speaking, both User or Shell Notifications API are intuitive enough; hence it isn’t worth spending too much time on it now. There are a lot of samples all over the Web in C/C++ or C# or VB. That’s not a point here. Real troubles start when a device gets turned off. And, here is when Power management can help us.

Involving Power Management

The Windows Mobile OS has a dozen functions that allow applications to control the power policy and query various information. Once again, I won’t discuss them too much, but move on to the situation described in the preface.

When a device has been suspended and after any notification gets activated, the system turns to a “resuming” power state. In such a mode, the CPU is running, but the display is not powered to save the battery. Moreover, some of the PDA’s features may be inactivated as well; for example, the GPRS modem. “Resuming” the power state continues for 15 seconds, and then, if nothing happens, a device will be suspended once again. Thus, you face the situation when your application is honestly called, but it can’t interact with the external world or gain the user’s attention. For background operations, it works like a charm, but for notifications, that is not the case. Thus, you need a way to turn on the system. The obvious answer is to call:

SetSystemPowerState(
   LPCWSTR pwsSystemState,
   DWORD   StateFlags,
   DWORD   Options
);

as follows:

SetSystemPowerState(NULL, POWER_STATE_ON, 0);

In other words, such a call simply wakes up the device and gets it to its full live state. That is exactly what you need in many cases. Later on, you also can call:

SetPowerRequirement(
   PVOID                   pvDevice,
   CEDEVICE_POWER_STATE    DeviceState,
   ULONG                   DeviceFlags,
   PVOID                   pvSystemState,
   ULONG                   StateFlags
);

to require an ‘always on’ state while doing some lengthly operation; for example, communications. And with that said, this story has its “happy ending.”

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories