Notifications and Power Management under Windows Mobile: Benefit from Both

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:

“\\.\Notifications\NamedEvents\Event 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=”http://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”\windows\calc.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.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read