free web page counters

Windows Mobile Pocket PC Smartphone Programming

==>Click here for the SiteMap<==. Original contents with decent amount of source codes.

Tuesday, December 20, 2005

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(&notifTrigger, 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, &notifTrigger, 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]

====>SiteMap of this Blog<===