Windows Mobile 6: Keep the Display On

Published:
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);

Open in new window


Do not forget to release handle when you close application:

ReleasePowerRequirement(hBacklight)

Open 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;
                      };

Open 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();
                      };

Open 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;
                      };

Open 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);
                      }

Open 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.

1
5,730 Views

Comments (1)

Mark WillsTopic Advisor
CERTIFIED EXPERT
Distinguished Expert 2018

Commented:
@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.

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.