Pocket PC Power Management Series 6: Request the Device to Run: a Gentle Way
====>SiteMap of this Blog<===
Pocket PC Power Management Series 6: Request the Device to Run: a Gentle Way
In Pocket PC Power Management Series 5, I showed how to use API SystemIdleTimerReset to force the device not to sleep. However, this technique is an overkill and a waste if used for case-2 programs (which need to run periodically, not continuously). Further, some utilities are known to disrupt SystemIdleTimerReset, for example, Keyguard.
A more gentle way is to request the device to run at certain time, by using the omnipotent CeSetUserNotificationEx (see my Pocket PC Power Management Series 4 for a brief introduction to this API). Hereby I present three ready-to-use functions.
1. How to Use?
If you want to run Pocket IE to browse a URL 10 minutes later, simply call:
ScheduleRunApp(TEXT("\\Windows\\iexplore.exe"), TEXT("URLXXX""), 600);
And remember to clear it after some time:
ClearRunApp(TEXT("\\Windows\\iexplore.exe"));
2. Walkthrough of the functions
- SecondsAfter: It takes a pointer to SYSTEMTIME and a number of seconds,;
upon returning, the SYSTEMTIME is modified to be exactly that number of
seconds after the original time. For example, if original time is 10:00AM
now, and you want to a time 10:05AM, just pass 300 as the second parameter.
The function converts SYSTEMTTIME to FILETIME for easy arithmetic operation.
static HRESULT SecondsAfter(SYSTEMTIME* pst, int numSeconds)
{
FILETIME ft;
// convert to FILETIME then ULONGLONG for calculation
if (!SystemTimeToFileTime(pst, &ft)) return E_FAIL;
ULONGLONG ulLongFt = *(ULONGLONG*)&ft;
ulLongFt += (ULONGLONG)numSeconds * 10000000;
// 10000000: how many nano seconds per second, divided by 100
// convert back to FILETIMEand SYSTEMTIME
ft = *(FILETIME*)&ulLongFt;
if (!FileTimeToSystemTime(&ft, pst)) return E_FAIL;
return S_OK;
}
- ScheduleRunApp: It takes an Executable name, arguments and a number of
seconds. All parameters are self-explanatory. For example, if you pass 300
as dwAfterSeconds, the program is scheduled to run 5 minutes after the
function is called.
static HRESULT ScheduleRunApp(
LPCTSTR szExeName,
LPCTSTR szArgs,
DWORD dwAfterSeconds)
{
HRESULT hr = S_OK;
HANDLE hNotify = NULL;
// set a CE_NOTIFICATION_TRIGGER
CE_NOTIFICATION_TRIGGER notifTrigger;
memset(¬ifTrigger, 0, sizeof(CE_NOTIFICATION_TRIGGER));
notifTrigger.dwSize = sizeof(CE_NOTIFICATION_TRIGGER);
// calculate time
SYSTEMTIME st = {0};
GetLocalTime(&st);
hr = SecondsAfter(&st, dwAfterSeconds);
if (FAILED(hr)) {
return hr;
}
notifTrigger.dwType = CNT_TIME;
notifTrigger.stStartTime = st;
// timer: execute an exe at specified time
notifTrigger.lpszApplication = (LPTSTR)szExeName;
notifTrigger.lpszArguments = (LPTSTR)szArgs;
hNotify = CeSetUserNotificationEx(0, ¬ifTrigger, NULL);
// NULL because we do not care the action
if (!hNotify) {
hr = E_FAIL;
} else {
// close the handle as we do not need to use it further
CloseHandle(hNotify);
}
return hr;
}
- ClearRunApp: I want to emphasize and repeat what I said in Pocket
PC Power Management Series 4 regarding CeSetUserNotificationEx. One
notorious side-effect of CeSetUserNotificationEx is that the notification
will be persisted in a central repository, unless the creator remembers to
remove it. I've seen a couple of novice developers forgot to clear the
notifications and subject his/her testing device to some really weird
behaviors. So I also present ClearRunApp, which is used to clear previously
scheduled tasks. Remember: Even if the task already ran, the notification is
still persisted, unless cleared programmatically.
static HRESULT ClearRunApp(LPCTSTR szExeName)
{
HRESULT hr = S_OK;
// hold a notification
PBYTE pBuff = (PBYTE)LocalAlloc(LPTR, 8192);
if (!pBuff) {
return E_OUTOFMEMORY;
}
// at most 256 notification handles
HANDLE hNotifHandlers[256];
DWORD nNumHandlers, nNumClearedHandlers = 0;
DWORD i = 0;
int rc = CeGetUserNotificationHandles(hNotifHandlers,
dim(hNotifHandlers), &nNumHandlers);
if (!rc) {
hr = E_FAIL;
goto FuncExit;
}
// iterate all notifications
// Notice: We do not care about the status of the notification.
// Just clear it even if it is not filed??
for (; i<nNumHandlers; i++) {
// query info for this specific handler
BOOL bClearThis = FALSE;
DWORD dwSize = 0;
rc = CeGetUserNotification(
hNotifHandlers[i], 8192, &dwSize, pBuff);
if (!rc) continue;
PCE_NOTIFICATION_INFO_HEADER pnih =
(PCE_NOTIFICATION_INFO_HEADER)pBuff;
PCE_NOTIFICATION_TRIGGER pNotifTrigger = pnih->pcent;
// Notice some events with NULL lpszApplication might be inserted!
if (pNotifTrigger && pNotifTrigger->lpszApplication
&& !_tcsicmp(pNotifTrigger->lpszApplication, szToCompare)) {
CeClearUserNotification(pnih->hNotification)
}
}
FuncExit:
if (pBuff) {
LocalFree(pBuff);
}
return hr;
}
3. Further thoughts
If you do not need the style of "running a program AFTER some time", rather, what you want is "running a program AT certain time", you can simply modify ScheduleRunApp as needed.
Category: [Power Management]