Windows Mobile 6: Keep the Display On

AID: 1255
  • Status: Published

2260 points

  • Bypgnatyuk
  • TypeTips/Tricks
  • Posted on2009-07-29 at 07:23:07
I think the most popular question in our Windows Mobile Programming zone is about the backlight - we all want to keep it on when our application is running.
Few years ago the function SystemIdleTimerReset() did this job. On our side we had to detect the device idle time and call this function appropriately. In Windows Mobile 6 this function keeps the system running, but the display is turned off.

How to keep the backlight on?

If you need to keep your device at full power, you can use the following code:

HANDLE hBacklight = SetPowerRequirement(L"BKL1:", D0, POWER_NAME, NULL, 0);
                                    
1:

Select allOpen in new window



Do not forget to release handle when you close application:

ReleasePowerRequirement(hBacklight)
                                    
1:

Select allOpen in new window



SystemIdleTimerReset() should be used also - this function keeps the device running. If you will forget about it, the display will be on, but the device will go to sleep together with your application. It looks like your application gets stuck.

More details about this approach and a working example you can find in MSDN: Program Applications to Turn the Smartphone Backlight Off and On


Bruce Eitman proposed a new solution in his article Windows CE: Keeping the Backlight On
.
He found out that the power manager is monitoring a named event. The name of this event can be obtained from the registry:

const static LPCWSTR s_szGWE_REG_PATH = L"SYSTEM\\GWE";
const static LPCWSTR s_szActivityKey = L"ActivityEvent";
 
DWORD EventName(LPWSTR& lpszName)
{
   HKEY hKey = NULL;
   DWORD nResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, s_szGWE_REG_PATH, 0, 0, &hKey);
   if (nResult != ERROR_SUCCESS)
      return 0;
 
   DWORD nNumBytes = 0;
   DWORD nType;
   nResult = RegQueryValueEx(hKey, s_szActivityKey, NULL, &nType, NULL, &nNumBytes);a
   if (nResult == ERROR_SUCCESS)
   {
      nResult = 0;
      if (nNumBytes > 0)
      {
         lpszName = (LPWSTR)malloc(nNumBytes + 2);
         if (lpszName != NULL)
         {
            ZeroMemory(lpszName, nNumBytes + 2);
            nResult = RegQueryValueEx(hKey, s_szActivityKey, NULL, &nType, (LPBYTE)lpszName, &nNumBytes);
            nResult = wcslen(lpszName);
         }
      }
      RegCloseKey(hKey);
   }
   return nResult;
};
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:

Select allOpen in new window



If we have the name, we can create this event and raise it periodically.
In order to test this approach I made a simple class CPowerEvent:

#pragma once
 
class CPowerEvent
{
   enum enumHandles
   {
      HANDLE_THREAD = 0,
      HANDLE_POWER_EVENT,
      HANDLE_STOP,
      HANDLE_START,
      HANDLE_EXIT,
      HANDLES
   };
 
   enum enumTime
   {
      TIME_EXIT_THREAD = 1000,
      TIME_IDLE        = 5000
   };
 
   LPWSTR   m_lpszName;
   HANDLE   m_arrHandles[HANDLES];
   DWORD   m_nIdle;
 
   static DWORD WINAPI Run(LPVOID pParam);
 
public:
   CPowerEvent();
   virtual ~CPowerEvent();
 
   static DWORD EventName(LPWSTR& lpszName);
 
   BOOL Start();
   BOOL Stop();
 
   BOOL SetIdle(DWORD nTime);
 
   inline BOOL IsActive() 
   { return (m_arrHandles[HANDLE_THREAD] != NULL); };
 
   void Clear();
};
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:

Select allOpen in new window



And the implementation is the following:

#include "StdAfx.h"
#include "PowerEvent.h"
 
const static LPCWSTR s_szGWE_REG_PATH   = L"SYSTEM\\GWE";
const static LPCWSTR s_szActivityKey   = L"ActivityEvent";
 
CPowerEvent::CPowerEvent()
{
   m_lpszName   = NULL;
   m_nIdle      = TIME_IDLE;
   ZeroMemory(m_arrHandles, HANDLES * sizeof(HANDLE));
}
 
CPowerEvent::~CPowerEvent()
{
   Clear();
}
 
void CPowerEvent::Clear()
{
   if (m_arrHandles[HANDLE_THREAD] != NULL)
   {
      SetEvent(m_arrHandles[HANDLE_EXIT]);
      DWORD nWait = WaitForSingleObject(m_arrHandles[HANDLE_THREAD], TIME_EXIT_THREAD);
      if (nWait != WAIT_OBJECT_0)
         TerminateThread(m_arrHandles[HANDLE_THREAD], 2);
   }
   int nCnt = 0;
   do 
   {
      if (m_arrHandles[nCnt] != NULL)
      {
         CloseHandle(m_arrHandles[nCnt]);
         m_arrHandles[nCnt] = NULL;
      }
      nCnt++;
   } while(nCnt < HANDLES);
 
   m_nIdle = TIME_IDLE;
 
   ZeroMemory(m_arrHandles, HANDLES * sizeof(HANDLE));
   if (m_lpszName != NULL)
   {
      free(m_lpszName);
      m_lpszName = NULL;
   }
}
 
DWORD CPowerEvent::EventName(LPWSTR& lpszName)
{
   HKEY hKey = NULL;
   DWORD nResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, s_szGWE_REG_PATH, 0, 0, &hKey);
   if (nResult != ERROR_SUCCESS)
      return 0;
 
   DWORD nNumBytes = 0;
   DWORD nType;
   nResult = RegQueryValueEx(hKey, s_szActivityKey, NULL, &nType, NULL, &nNumBytes);
   if (nResult == ERROR_SUCCESS)
   {
      nResult = 0;
      if (nNumBytes > 0)
      {
         lpszName = (LPWSTR)malloc(nNumBytes + 2);
         if (lpszName != NULL)
         {
            ZeroMemory(lpszName, nNumBytes + 2);
            nResult = RegQueryValueEx(hKey, s_szActivityKey, NULL, &nType, (LPBYTE)lpszName, &nNumBytes);
            nResult = wcslen(lpszName);
         }
      }
      RegCloseKey(hKey);
   }
   return nResult;
}
 
BOOL CPowerEvent::Start()
{
   if (m_arrHandles[HANDLE_THREAD] != NULL)
   {
      SetEvent(m_arrHandles[HANDLE_START]);
      return TRUE;
   }
 
   if (m_lpszName == NULL)
   {
      DWORD nSize = EventName(m_lpszName);
      if (nSize == 0 || m_lpszName == NULL)
         return FALSE;
   }
 
   m_arrHandles[HANDLE_POWER_EVENT] = CreateEvent(NULL, FALSE, FALSE, m_lpszName);
   if (m_arrHandles[HANDLE_POWER_EVENT] == NULL)
      return FALSE;
 
   int nCnt = HANDLE_STOP;
   while (nCnt < HANDLES)
   {
      m_arrHandles[nCnt] = CreateEvent(NULL, FALSE, FALSE, NULL);
      nCnt++;
   }
   m_arrHandles[HANDLE_THREAD] = CreateThread(NULL, 0, Run, (LPVOID)this, 0, NULL);
   if (m_arrHandles[HANDLE_THREAD] == NULL)
   {
      Clear();
      return FALSE;
   }
 
   return TRUE;
}
 
BOOL CPowerEvent::Stop()
{
   if (m_arrHandles[HANDLE_THREAD] == NULL)
      return FALSE;
   SetEvent(m_arrHandles[HANDLE_STOP]);
   return TRUE;
}
 
BOOL CPowerEvent::SetIdle(DWORD nTime)
{
   if (m_arrHandles[HANDLE_THREAD] != NULL)
      return FALSE;
   m_nIdle = nTime;
   return TRUE;
}
 
DWORD WINAPI CPowerEvent::Run(LPVOID pParam)
{
   CPowerEvent* pSelf = (CPowerEvent*)pParam;
 
   DWORD nIdle = TIME_IDLE;
   if (pSelf->m_nIdle > 0 && pSelf->m_nIdle != nIdle)
      nIdle = pSelf->m_nIdle;
 
   HANDLE arrWait[2] = { pSelf->m_arrHandles[HANDLE_STOP], pSelf->m_arrHandles[HANDLE_EXIT] };
   HANDLE arrPause[2] = { pSelf->m_arrHandles[HANDLE_START], pSelf->m_arrHandles[HANDLE_EXIT] };
 
   DWORD nWait = WAIT_OBJECT_0;
   BOOL bExit = FALSE;
 
   do 
   {
      SetEvent(pSelf->m_arrHandles[HANDLE_POWER_EVENT]);
 
      nWait = WaitForMultipleObjects(2, arrWait, FALSE, nIdle);
      if (nWait == WAIT_OBJECT_0)
      {
         nWait = WaitForMultipleObjects(2, arrPause, FALSE, INFINITE);
         if (nWait == WAIT_OBJECT_0 + 1)
            bExit = TRUE;
      }
 
      else if (nWait == WAIT_OBJECT_0 + 1)
         bExit = TRUE;
 
   } while(!bExit);
 
   return 0;
};
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:

Select allOpen in new window



My test application is a simple dialog-based MFC application. I added CPowerEven oject into the dialog class as a private member. On the dialog form I put 2 buttons: On and Off. The buttons' click handlers are trivial:
1. Button "On" calls method Start() from the CPowerEvent object.
2. Button "Off" calls method Stop().

I tested this approach with the power event on few Windows Mobile 6.1 devices. I found one device where this code didn't work. The approach described in the beginning of the article fails also. Probably, the device has a different power manager, as can be the case with OEM devices.

Like always, the simplest solution for this "Display On" ("Backlight On") problem can be found on the application level - the device is on all the time when the user type something on the keyboard. So, if I will periodically emulate the keyboard input the device will never "go to sleep".

The following code emulate the keyboard input that keeps the device on:

void EmulateKeyboard()
{
   EnableHardwareKeyboard(FALSE);
   keybd_event(0xE9, 0, KEYEVENTF_SILENT, 0);
   keybd_event(0xE9, 0, KEYEVENTF_SILENT | KEYEVENTF_KEYUP, 0);
}
                                    
1:
2:
3:
4:
5:
6:

Select allOpen in new window



Few more important points about the subject of this article:

1. We need to remember that we are working with the mobile devices and the effective power management is a critical issue. The code represented here does not save the battery. It does not overload the CPU, but keeping the backlight on all the time is, definitely, against all Microsoft recommendations for power effective applications.

2. I assume that tomorrow I will see another device with Windows Mobile (or CE) 5 or 6 where all described techniques will not work. OEMs (device manufacturers) always build and tailor their own platforms for their devices. They adopt the operating system for their hardware. In many cases OEM's develop their own drivers based on the Microsoft samples. It is even not necessary to follow the Microsoft recommendations. So due to the customization an OEM may have done, the programs that use the registry information, OEM's keyboard codes, etc., as the examples in this article, may not work on a specific Windows CE/Mobile device.

Asked On
2009-07-29 at 07:23:07ID1255
Tags

Visual C++

,

Windows Mobile

,

Power Management

Topic

Windows MobileProgramming

Views
1662

Comments

Expert Comment

by: mark_wills on 2009-07-30 at 21:34:15ID: 2440

@pgnatyuk:

Articles when first submittted are regarded as "unverified" in so much as none of the Editors have seen it yet.

As an article is worked on it should swap status between Editor Review and Author Review.

As aikimark has already pointed out, it really was a message for him. Sorry for any confusion.

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top Windows Mobile Experts

  1. Mikal613

    60,718

    Master

    0 points yesterday

    Profile
    Rank: Genius
  2. hjgode

    46,700

    0 points yesterday

    Profile
    Rank: Guru
  3. alexey_gusev

    11,876

    0 points yesterday

    Profile
    Rank: Genius
  4. CodeCruiser

    7,400

    0 points yesterday

    Profile
    Rank: Genius
  5. ambience

    2,800

    0 points yesterday

    Profile
    Rank: Sage
  6. AndyAinscow

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  7. sasllc

    2,000

    0 points yesterday

    Profile
  8. Programmer-x

    2,000

    0 points yesterday

    Profile
    Rank: Guru
  9. Nigel_R

    2,000

    0 points yesterday

    Profile
  10. ve3ofa

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  11. mkline71

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  12. nepaluz

    2,000

    0 points yesterday

    Profile
    Rank: Sage
  13. acbrown2010

    2,000

    0 points yesterday

    Profile
    Rank: Genius
  14. Jamielive2011

    2,000

    0 points yesterday

    Profile
    Rank: Master
  15. TheLearnedOne

    1,800

    0 points yesterday

    Profile
    Rank: Savant
  16. athomsfere

    1,800

    0 points yesterday

    Profile
    Rank: Wizard
  17. minhvc

    1,500

    0 points yesterday

    Profile
    Rank: Wizard
  18. demazter

    1,500

    0 points yesterday

    Profile
    Rank: Genius
  19. degaray

    1,500

    0 points yesterday

    Profile
  20. Bardobrave

    1,500

    0 points yesterday

    Profile
    Rank: Sage
  21. dj_alik

    1,500

    0 points yesterday

    Profile
    Rank: Sage
  22. jason1178

    1,400

    0 points yesterday

    Profile
    Rank: Genius
  23. alanhardisty

    1,350

    0 points yesterday

    Profile
    Rank: Genius
  24. pgnatyuk

    1,250

    0 points yesterday

    Profile
    Rank: Genius
  25. dons6718

    1,000

    0 points yesterday

    Profile
    Rank: Sage

Hall Of Fame