A Windows CE application can schedule the user to be notified at a given time using the CeSetUserNotificationEx function. When the time of the notification occurs, the system alerts the user by displaying a dialog box, playing a wave file, vibrating the device, or flashing an external LED. If the system was off at the time of the notification, Windows CE turns the system on. Because Windows CE systems have an automatic power-off feature, the system will quickly turn itself back on if the notification fires while the system is unattended. Figure 11-1 shows the alert bubble on a Pocket PC, while Figure 11-2 shows the notification dialog on an embedded Windows CE device.
Figure 11-1: The alert bubble on a Pocket PC device
Figure 11-2: The notification dialog on an embedded Windows CE device
Windows CE also displays the icon of the application that set the notification on the taskbar. The user has the option of acknowledging the notification by clicking OK on the notification dialog box, pressing the Notify button on the system case (if one is present), or on some systems, tapping the application's taskbar annunciator icon, which launches the application that registered the notification. After a user notification has been set, you can modify it by making another call to CeSetUserNotificationEx.
CeSetUserNotificationEx is prototyped as
HANDLE CeSetUserNotificationEx (HANDLE hNotification, CE_NOTIFICATION_TRIGGER *pcnt, CE_USER_NOTIFICATION *pceun);
The hNotification parameter is set to 0 to create a new notification. To modify a notification already registered, you should set hNotification to the handle of the notification that you want to modify.
The CE_NOTIFICATION_TRIGGER structure defines the type and detail of the notification being set. This structure is defined as
typedef struct UserNotificationTrigger { DWORD dwSize; DWORD dwType; DWORD dwEvent; WCHAR *lpszApplication; WCHAR *lpszArguments; SYSTEMTIME stStartTime; SYSTEMTIME stEndTime; } CE_NOTIFICATION_TRIGGER, *PCE_NOTIFICATION_TRIGGER;
The first field should be set to the size of the structure. The second field, dwType, should be filled with a flag indicating the type of notification being set. For user notifications, set this field to either CNT_PERIOD or CNT_TIME. The CNT_PERIOD flag creates a notification that will dismiss itself after a set time, while a CNT_TIME notification will not dismiss itself without user action. For user notifications, the dwEvent field isn't used. I'll talk about that field when I discuss event notifications.
The next field, lpszApplication, specifies the application that will be launched if the user requests more detail from the notification. If the application is launched, its command line is specified by the next field, lpszArguments.
Another use for the lpszApplication field is to specify an event to be signaled when the notification fires. To specify an event, the field should be formatted as
\\.\Notifications\NamedEvents\<Event Name>
where <Event Name> is any name chosen for the event. Remember that when you specify this string in C, the backslash character must be replicated because it's used as the escape character. So to have a notification trigger an event named Bob, the string pointed to by the lpszApplication field would look like this:
TEXT ("\\\\.\\Notifications\\NamedEvents\\Bob")
To be notified using an event, an application must create a named event with the same name as <Event Name> by using the CreateEvent function and wait on the handle returned, as in
hEvent = CreateEvent (NULL, FALSE, FALSE, TEXT ("Bob"));
The final two fields, stStartTime and stEndTime, specify the starting time and ending time of the notice. The starting time, of course, is when the system first notifies the user by means of a number of different methods I'll talk about in a moment. You use the ending time only in a CNT_PERIOD-style user notification; the CeSetUserNotificationEx function ignores the ending time for CNT_TIME notifications. stEndTime designates the time the system is to remove the notice if the user doesn't acknowledge the notification. This time must be later than the starting time.
How the system notifies the user is specified by the third parameter of CeSetUserNotificationEx, which points to a CE_USER_NOTIFICATION structure. This structure is defined as
typedef struct UserNotificationType { DWORD ActionFlags; TCHAR *pwszDialogTitle; TCHAR *pwszDialogText; TCHAR *pwszSound; DWORD nMaxSound; DWORD dwReserved; } CE_USER_NOTIFICATION;
The ActionFlags field of this structure contains a set of flags that define how the user is notified. The flags can be any combination of the following:
PUN_LED Flash the external LED.
PUN_VIBRATE Vibrate the device.
PUN_DIALOG Display a dialog box.
PUN_REPEATRepeat the wave file for 10 to 15 seconds.
The fact that these flags are defined doesn't mean that all systems implement all these actions. Most Windows CE devices can't vibrate and a few don't even have an external LED. There isn't a defined method for determining the notification capabilities of a device, but as I'll presently show you, the system provides a dialog box that's customized by the OEM for the capabilities of each device.
The remainder of the fields in the structure depend on the flags set in the ActionFlags field. If the PUN_DIALOG flag is set, the pwszDialogTitle and pwszDialogText fields specify the title and text of the dialog that's displayed. For a Pocket PC device, the dialog text appears on the Alert dialog, but since the Pocket PC Alert doesn't use a caption bar, the dialog title text isn't used. The pwszSound field is loaded with the filename of a wave file to play if the PUN_SOUND flag is set. The nMaxSound field defines the size of the pwszSound field.
To give you a consistent user interface for choosing the method of notification, Windows CE provides a dialog box to query the user about how he wants to be notified. To display the user configuration dialog box, you call this function:
BOOL CeGetUserNotificationPreferences (HWND hWndParent, PCE_USER_NOTIFICATION lpNotification);
This function takes two parameters—the window handle of the parent window for the dialog box and a pointer to a CE_USER_NOTIFICATION structure. You can initialize the CE_USER_NOTIFICATION structure with default settings for the dialog before CeGetUserNotificationPreferences is called. When the function returns, this structure is filled with the changes the user made. CeGetUserNotificationPreferences returns TRUE if the user clicked the OK button to accept the changes and FALSE if an error occurred or the user canceled the dialog box. Figure 11-3 shows the notification preferences dialog box opened through the CeGetUserNotificationPreferences function on a Pocket PC.
Figure 11-3: The dialog box opened by CeGetUserNotificationPreferences on a Pocket PC
This function gives you a convenient method for configuring user notifications. The dialog box lets you have check boxes for playing a sound, displaying another dialog box, and flashing the LED. It also contains a combo box that lists the available wave files that the user can choose from if he wants sound. The dialog box doesn't have fields to allow the user to specify the text or title of the dialog box if one is to be displayed. That text must be provided by the application.
A user notification can be cleared by the application before it times out by calling
BOOL CeClearUserNotification (HANDLE hNotification);
Once a user notification has occurred, it must be acknowledged by the user unless the user notification's end time has passed. The user can tap the Dismiss button on the notification dialog box or press the notification button on the Pocket PC case. Or the user can tap the Snooze button, which automatically reschedules the notification for a later time. On an H/PC or an embedded Windows CE system, the user can tap the Open button to launch the application specified when the notification was scheduled. An Open button isn't provided on the alert dialog on the current implementations of the Pocket PC.
If the user taps the Open button, the notification isn't automatically acknowledged. Instead, an application should programmatically acknowledge the notification by calling this function:
BOOL CeHandleAppNotifications (TCHAR *pwszAppName);
The one parameter is the name of the application that was launched because the user tapped the Open button. Calling this function removes the dialog box, stops the sound, turns off the flashing LED, and on systems with the Windows CE Explorer shell, removes the application's annunciator icon from the taskbar. This function doesn't affect any notifications that are scheduled but haven't fired.
When the system starts an application because of a notification, it passes a command line argument to indicate why the application was started. For a user notification, this argument is the command line string specified in the lpszArguments field of the CE_NOTIFICATION_TRIGGER structure. If you scheduled the notification using the CNT_CLASSICTIME flag, the command line is the predefined string constant APP_RUN_TO_HANDLE_NOTIFICATION. If the event notification method is specified, the application won't be started. Instead, an event of the specified name will be signaled.
As a general rule, an application started by a notification should first check to see whether another instance of the application is running. If so, the application should communicate to the first instance that the notification occurred and terminate. This saves memory because only one instance of the application is running. The following code fragment shows how this can be easily accomplished.
INT i; HWND hWnd; HANDLE hNotify; TCHAR szText[128]; TCHAR szFileName[MAX_PATH]; if (*lpCmdLine) { pPtr = lpCmdLine; // Parse the first word of the command line. for (i = 0; i < dim(szText) && *lpCmdLine > TEXT (' '); i++) szText[i] = *pPtr++; szText[i] = TEXT ('\0'); // Check to see if app started due to notification. if (lstrcmp (szText, TEXT("My Notification cmdline")) == 0) { // Acknowledge the notification. GetModuleFileName (hInst, szFileName, sizeof (szFileName)); CeHandleAppNotifications (szFileName); // Get handle off the command line. hNotify = (HANDLE)_wtol (pPtr); // Look to see if another instance of the app is running. hWnd = FindWindow (NULL, szAppName); if (hWnd) { SendMessage (hWnd, MYMSG_TELLNOTIFY, 0, (LPARAM)hNotify); // This app should terminate here. return 0; } } }
This code first looks to see whether a command line parameter exists and if so, whether the first word is the keyword indicating that the application was launched by the system in response to a user notification. If so, the notification is acknowledged and the application looks for an instance of the application already running, using FindWindow. If found, the routine sends an application-defined message to the main window of the first instance and terminates. Otherwise, the application can take actions necessary to respond to the user's tap of the Open button on the alert dialog.