\

Školení Návrhové vzory, OOP a UML


 Monday, 01 December 2008
Běh aplikace na Windows Mobile při vypnutém displeji a opětovné probuzení zařízení

htc-touch-pro Jedním z problémů, se kterými se vývojáři často potýkají, je, že PDA (přesněji 7Windows Mobile Professional a Classic zařízení  - zařízení s dotykovým displejem) na rozdíl od Smartphonu (Windows Mobile Standard) po uplynutí doby nastavené v ovládacích panelech přecházejí do stavu "Off" (přesněji do stavu "Suspended" dle oficiální terminologie, stav Off má pouze Smartphone - my ale budeme dále ve spotu používat termín "off") , v němž  je provádění kódu aplikace zcela "zmrazeno". Aplikace jsou "hibernovány" a můžeme je považovat za dočasné mrtvolky.

Občas potřebujeme, aby sice došlo k vypnutí displeje zařízení a my tak drasticky a navíc zbytečně neredukovali výdrž baterie, ale aby naše aplikace na pozadí stále běžela. To je první  požadavek. Dalším z častých požadavků je schopnost aplikace probudit celé zařízení ze stavu "off".

Jestliže nám jde pouze o probuzení aplikace po uplynutí námi nastaveného času či při nějaké události (připojení k síti, změna proxy apod.), je řešení jednoduché - v našem arzenálu bude hrát prim omnipotentní funkce CeSetUserNotificationEx.

Když chceme v naší aplikaci pracovat i při vypnutém displeji (např. odpočítáváme sekundy v aplikaci "minutka") a současně probudit zařízení poté, co uběhla nastavená doba, musíme se začít přátelit s dalšími mocnými, avšak ke škodě samotné platformy Windows Mobile mizerně dokumentovanými API.

Co musíme zajistit:

  1. Zařízení nesmí přejít do "off" módu, ale musíme jej ponechat ve speciálním stavu, kdy je vypnutý displej, ale aplikace, které potřebují běžet, nejsou "zmrazeny". Námi požadovaný speciální stav se na Windows Mobile nazývá "unattended mód". Z hlediska uživatele je při "unattended módu" zařízení vypnuto, ale naše aplikace, která si o tento mód požádala, pokračuje ve své činnosti.


    K tomu nám poslouží API PowerPolicyNotify.
    PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_ON).
    Funkci PowerPolicyNotify předáme v prvním argumentu požadavek na Unattended mód a ve druhém argumentu konstantu UNATTENDED_ON s hodnotou 1 - TRUE.

  2. V unattended módu chceme zařízení zcela probudit. Opět použijeme API PowerPolicyNotify.
    PowerPolicyNotify(PowerMode.AppButtonPressed, 0)
    Tentokrát ale předáme konstantu PowerMode.AppButtonPressed, která simuluje stisknutí tlačítka "On/Off" na zařízení. Druhý argument musí být 0, funkcí s ním  při tomto volání nepracuje.

  3. Jestliže již nepotřebujeme běžet v "unattended módu", měli bychom dovolit zařízení, aby naši aplikaci hibernovalo. Nejpozději při ukončení aplikace se tedy musíme vzdát "unattended módu". Použijeme opět API PowerPolicyNotify s prvním argumentem PowerMode.UnattendedMode a ve druhém argumentu předáme konstantu UNATTENDED_OFF s hodnotou 0 - FALSE.

    PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_OFF).
    Sice jsem si ověřil, že při ukončení procesu je "unattended mód" zrušen samotnými Windows, ale je vždy příjemné  pracovat s kódem, v němž nejsou obvyklé  "prasácké" zlozvyky v  managed kódu, a nenechat tedy v naší situaci uklízet jen Garbage Collector nebo správce procesů ve Windows.

V následujícím příkladu naleznete také tento zakomentovaný kód:

 

powerSettings = SetPowerRequirement("BKL1:", (int)DeviceState.FullOn, POWER_FORCE, IntPtr.Zero, 0);

                if (powerSettings == IntPtr.Zero)
                {
                    MessageBox.Show("Failed"); 
                }

K zapnutí displeje je použito API SetPowerRequirement. Toto API ale v "unattended" módu selhává - vždy mi na zařízeních, která mám doma, vrací false. Dále musíte znát zkratku zařízení, pro které chcete nastavit nový režim. Na většině zařízení je displej identifikován zkratkou BKL1: - abyste si ale byli jisti, musíte název najít v registrech - klíče pod HKLM\Drivers\Active\*. API PowerPolicyNotify je tedy pro naše účely mnohem vhodnější.

Pozor: Některá zařízení (pokud se dobře pamatuji, tak např. MIO) nemají od výrobce podporu "unattended" módu a aplikace je stejně vždy zmrazena.

Zde je slíbený příklad - na formuláři je pouze listbox, do kterého je každých 5 vteřin přidáno další číslo. Můžete si ověřit, že čísla přibývají i v "unattended" módu. Po uplynutích každých 75 vteřin (konstanta WAKE_UP_COUNTER  (15) * 5) je zařízení opakovaně probuzeno k "plnému vědomí", což se u něj projeví rozsvíceným displejem. :-)

V ovládacích panelech (záložka Systém, položka Napájení, ikona Upřesnit) nastavte vypnutí displeje i zařízení po uplynutí jedné minuty neaktivity, abyste si příklad užili. ;-)

 

 

using System.Runtime.InteropServices;
using System.Windows.Forms;
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;

namespace PowerPDAProject
{
    public delegate void MethodInvoker();
    public partial class Form1 : Form
    {
        
        public const int UNATTENDED_ON = 1;
        public const int POWER_FORCE = 0x1000;
        public const int UNATTENDED_OFF = 0;

        public const int  WAKE_UP_COUNTER = 15; 
        private int m_secondsCount;
        private IntPtr powerSettings;
        
        public enum PowerMode
        {
            ReevaluateStat = 0x0001,
            PowerChange = 0x0002,
            UnattendedMode = 0x0003,
            SuspendKeyOrPwrButtonPressed = 0x0004,
            SuspendKeyReleased = 0x0005,
            AppButtonPressed = 0x0006
        }

        [DllImport("CoreDll.dll")]
        public static extern int PowerPolicyNotify(PowerMode powerMode, int flags);


        public enum DeviceState : int
        {

            Unspecified = -1,
            FullOn = 0,
            LowOn,
            StandBy,
            Sleep,
            Off,
            Maximum

        }
               

            [DllImport("CoreDll.DLL")]
            public static extern IntPtr SetPowerRequirement(String pvDevice, int DeviceState, int DeviceFlags, IntPtr pvSystemState, int StateFlags);


            [DllImport("CoreDll.DLL")]
            public static extern uint ReleasePowerRequirement(IntPtr hPowerReq);

        



        public Form1()
        {
            
            InitializeComponent();
            m_secondsCount = 0;
        }

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bool result = (PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_ON) != 0);
            MessageBox.Show(result.ToString());
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            m_secondsCount++;
            listBox1.Invoke((MethodInvoker)(
                                            () => listBox1.Items.Add(m_secondsCount.ToString())));

            if (m_secondsCount % WAKE_UP_COUNTER == 0)
            {

                PowerPolicyNotify(PowerMode.AppButtonPressed, 0);
                //powerSettings = SetPowerRequirement("BKL1:", (int)DeviceState.FullOn, POWER_FORCE, IntPtr.Zero, 0);

                //if (powerSettings == IntPtr.Zero)
                //{
                //    MessageBox.Show("Failed"); 
                //}
            }
        }

        private void Form1_Closing(object sender, CancelEventArgs e)
        {
            timer1.Enabled = false;
            timer1.Dispose();
            PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_OFF);
            ReleasePowerRequirement(powerSettings);
            
        }
    }
}


Monday, 01 December 2008 18:23:53 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky