How to Manage the Power Settings of a Windows CE device using C# .NET CF

Published by Utkarsh Patel on 19/04/09 01:26:15
Last edited on 20/04/09 16:01:09

This article will show you how to manage the power settings of your Windows CE device using .NET Compact Framework. This C# class will allow you to suspend, turn off, idle and reset a Windows CE device. It will even allow you to manage the back light of the screen and monitor the battery power level.

Click here to see how to use this class

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;
using System.Threading;

namespace PowerManager
{
    /// <summary>
    /// PowerManager provides several methods to monitor and control 
    /// system and device(example: backlight, audio etc) power states. PowerManager  
    /// does this by wrapping Microsoft's Power Management API. For more information on 
    /// Power Management please refer to MSDN.
    /// </summary>
    /// <remarks>
    /// PowerMangement should be used with extreme caution as it may result in 
    /// unexpected behavior. 
    /// </remarks>
    public class PowerManager : IDisposable
    {
        #region ------------------Enumerations--------------------

        /// <summary>
        /// Defines the System power states
        /// </summary>
        public enum SystemPowerStates : uint
        {
            /// <summary>
            /// On state.
            /// </summary>
            On = 0x00010000,

            /// <summary>
            /// No power, full off.
            /// </summary>
            Off = 0x00020000,

            /// <summary>
            /// Critical off.
            /// </summary>
            Critical = 0x00040000,

            /// <summary>
            /// Boot state.
            /// </summary>
            Boot = 0x00080000,

            /// <summary>
            /// Idle state.
            /// </summary>
            Idle = 0x00100000,

            /// <summary>
            /// Suspend state.
            /// </summary>
            Suspend = 0x00200000,

            /// <summary>
            /// Reset state.
            /// </summary>
            Reset = 0x00800000
        }

        /// <summary>
        /// Defines the System power requirement flags
        /// </summary>
        public enum PowerReqFlags : uint
        {
            POWER_NAME = 0x00000001,
            POWER_FORCE = 0x00001000,
        }

        /// <summary>
        /// Defines the Device power states
        /// </summary>
        public enum DevicePowerStates
        {
            PwrDeviceUnspecified = -1,
            FullOn = 0,		// Full On: full power,  full functionality
            D0 = FullOn,
            LowOn,			// Low Power On: fully functional at low power/performance
            D1 = LowOn,
            StandBy,		// Standby: partially powered with automatic wake
            D2 = StandBy,
            Sleep,			// Sleep: partially powered with device initiated wake
            D3 = Sleep,
            Off,			// Off: unpowered
            D4 = Off,
            PwrDeviceMaximum
        }

        /// <summary>
        /// Defines the Power Status message type.
        /// </summary>
        [FlagsAttribute()]
        public enum MessageTypes : uint
        {
            /// <summary>
            /// System power state transition.
            /// </summary>
            Transition = 0x00000001,

            /// <summary>
            /// Resume from previous state.
            /// </summary>
            Resume = 0x00000002,

            /// <summary>
            /// Power supply switched to/from AC/DC.
            /// </summary>
            Change = 0x00000004,

            /// <summary>
            /// A member of the POWER_BROADCAST_POWER_INFO structure has changed.
            /// </summary>
            Status = 0x00000008
        }

        /// <summary>
        /// Defines the AC power status flags.
        /// </summary>
        public enum ACLineStatus : byte
        {
            /// <summary>
            /// AC power is offline.
            /// </summary>
            Offline = 0x00,

            /// <summary>
            /// AC power is online. 
            /// </summary>
            OnLine = 0x01,

            /// <summary>
            /// AC line status is unknown.
            /// </summary>
            Unknown = 0xff
        }

        /// <summary>
        /// Defines the Battery charge status flags.
        /// </summary>
        [FlagsAttribute()]
        public enum BatteryFlags : byte
        {
            /// <summary>
            /// High
            /// </summary>
            High = 0x01,

            /// <summary>
            /// Low
            /// </summary>
            Low = 0x02,

            /// <summary>
            /// Critical
            /// </summary>
            Critical = 0x04,

            /// <summary>
            /// Charging
            /// </summary>
            Charging = 0x08,

            /// <summary>
            /// Reserved1
            /// </summary>
            Reserved1 = 0x10,

            /// <summary>
            /// Reserved2
            /// </summary>
            Reserved2 = 0x20,

            /// <summary>
            /// Reserved3
            /// </summary>
            Reserved3 = 0x40,

            /// <summary>
            /// No system battery
            /// </summary>
            NoBattery = 0x80,

            /// <summary>
            /// Unknown status
            /// </summary>
            Unknown = High | Low | Critical | Charging | Reserved1 | Reserved2 | Reserved3 | NoBattery
        }

        /// <summary>
        /// Responses from <see cref="WaitForMultipleObjects"/> function.
        /// </summary>
        private enum Wait : uint
        {
            /// <summary>
            /// The state of the specified object is signaled.
            /// </summary>
            Object = 0x00000000,
            /// <summary>
            /// Wait abandoned.
            /// </summary>
            Abandoned = 0x00000080,
            /// <summary>
            /// Wait failed.
            /// </summary>
            Failed = 0xffffffff,
        }


        #endregion -----------------Enumerations-------------------

        #region --------------------Members-----------------------

        /// <summary>
        /// Indicates that an application would like to receive all types of 
        /// power notifications.
        /// </summary>
        private const uint POWER_NOTIFY_ALL = 0xFFFFFFFF;

        /// <summary>
        /// Indicates an infinite wait period
        /// </summary>
        private const int INFINITE = -1;

        /// <summary>
        /// Allocate message buffers on demand and free the message buffers after they are read.
        /// </summary>
        private const int MSGQUEUE_NOPRECOMMIT = 1;

        /// <summary>
        /// Event to wake up the worker thread so that it can close
        /// </summary>
        private AutoResetEvent powerThreadAbort;

        /// <summary>
        /// Flag requesting worker thread closure
        /// </summary>
        private bool abortPowerThread = false;

        /// <summary>
        /// Flag to indicate that the worker thread is running
        /// </summary>
        private bool powerThreadRunning = false;

        /// <summary>
        /// Thread interface queue
        /// </summary>
        private Queue powerQueue;

        /// <summary>
        /// Handle to the message queue
        /// </summary>
        private IntPtr hMsgQ = IntPtr.Zero;

        /// <summary>
        /// Handle returned from RequestPowerNotifications
        /// </summary>
        private IntPtr hReq = IntPtr.Zero;

        /// <summary>
        /// Boolean used to indicate if the object has been disposed
        /// </summary>
        private bool bDisposed = false;

        /// <summary>
        /// Occurs when there is some PowerNotify information available.
        /// </summary>
        public event EventHandler PowerNotify;

        #endregion --------------------Members--------------------

        #region -------------------Structures---------------------

        /// <summary>
        /// Contains information about a message queue.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct MessageQueueOptions
        {
            /// <summary>
            /// Size of the structure in bytes.
            /// </summary>
            public uint Size;

            /// <summary>
            /// Describes the behavior of the message queue. Set to MSGQUEUE_NOPRECOMMIT to 
            /// allocate message buffers on demand and to free the message buffers after 
            /// they are read, or set to MSGQUEUE_ALLOW_BROKEN to enable a read or write 
            /// operation to complete even if there is no corresponding writer or reader present.
            /// </summary>
            public uint Flags;

            /// <summary>
            /// Number of messages in the queue.
            /// </summary>
            public uint MaxMessages;

            /// <summary>
            /// Number of bytes for each message, do not set to zero.
            /// </summary>
            public uint MaxMessage;

            /// <summary>
            /// Set to TRUE to request read access to the queue. Set to FALSE to request write 
            /// access to the queue.
            /// </summary>
            public uint ReadAccess;
        };

        /// <summary>
        /// Contains information about the power status of the system  
        /// as received from the Power Status message queue.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct PowerInfo
        {
            /// <summary>
            /// Defines the event type.
            /// </summary>
            /// <see cref="MessageTypes"/>
            public MessageTypes Message;

            /// <summary>
            /// One of the system power flags.
            /// </summary>
            /// <see cref="SystemPowerStates"/>
            public SystemPowerStates Flags;

            /// <summary>
            /// The byte count of SystemPowerState that follows. 
            /// </summary>
            public uint Length;

            /// <summary>
            /// Levels available in battery flag fields
            /// </summary>
            public uint NumLevels;

            /// <summary>
            /// Number of seconds of battery life remaining, 
            /// or 0xFFFFFFFF if remaining seconds are unknown.
            /// </summary>
            public uint BatteryLifeTime;

            /// <summary>
            /// Number of seconds of battery life when at full charge, 
            /// or 0xFFFFFFFF if full battery lifetime is unknown.
            /// </summary>
            public uint BatteryFullLifeTime;

            /// <summary>
            /// Number of seconds of backup battery life remaining, 
            /// or BATTERY_LIFE_UNKNOWN if remaining seconds are unknown.
            /// </summary>
            public uint BackupBatteryLifeTime;

            /// <summary>
            /// Number of seconds of backup battery life when at full charge, 
            /// or BATTERY_LIFE_UNKNOWN if full battery lifetime is unknown.
            /// </summary>
            public uint BackupBatteryFullLifeTime;

            /// <summary>
            /// AC power status. 
            /// </summary>
            /// <see cref="ACLineStatus"/>
            public ACLineStatus ACLineStatus;

            /// <summary>
            /// Battery charge status. 
            /// </summary>
            /// <see cref="BatteryFlags"/>
            public BatteryFlags BatteryFlag;

            /// <summary>
            /// Percentage of full battery charge remaining. 
            /// This member can be a value in the range 0 (zero) to 100, or 255 
            /// if the status is unknown. All other values are reserved.
            /// </summary>
            public byte BatteryLifePercent;

            /// <summary>
            /// Backup battery charge status. 
            /// </summary>
            public byte BackupBatteryFlag;

            /// <summary>
            /// Percentage of full backup battery charge remaining. 
            /// This value must be in the range of 0 to 100, or BATTERY_PERCENTAGE_UNKNOWN.
            /// </summary>
            public byte BackupBatteryLifePercent;
        };

        #endregion -------------------Structures------------------

        #region ---------------------Methods----------------------

        /// <summary>
        /// Ensures that resources are freed when the garbage collector reclaims the object.
        /// </summary>
        ~PowerManager()
        {
            Dispose();
        }

        /// <summary>
        /// Releases the resources used by the object.
        /// </summary>
        public void Dispose()
        {
            if (!bDisposed)
            {
                // Try disabling notifications and ending the thread
                DisableNotifications();
                bDisposed = true;

                // SupressFinalize to take this object off the finalization queue 
                // and prevent finalization code for this object from executing a second time.
                GC.SuppressFinalize(this);
            }
        }

        /// <summary>
        /// Sets the system power state to the requested value.
        /// </summary>
        /// <param name="systemState">The system power state to set the device to.</param>
        /// <returns>Win32 error code</returns>
        /// <remarks>Should be used with extreme care since it may result in an unexpected 
        /// application or system behavior.</remarks>
        public int SetSystemPowerState(SystemPowerStates systemState)
        {
            uint nError = 0;

            nError = CESetSystemPowerState(
                IntPtr.Zero,
                (uint)systemState,
                0);

            return (int)nError;
        }

        /// <summary>
        /// Returns the current system power state currently in effect.
        /// </summary>
        /// <param name="systemStateName">Receives the system power state name</param>
        /// <param name="systemState">Receives the system power state</param>
        /// <returns>Win32 error code</returns>
        public int GetSystemPowerState(StringBuilder systemStateName, out SystemPowerStates systemState)
        {
            uint nError = 0;

            nError = CEGetSystemPowerState(systemStateName, (uint)systemStateName.Capacity, out systemState);

            return (int)nError;
        }

        /// <summary>
        /// Requests that the Power Manager change the power state of a device.
        /// </summary>
        /// <param name="deviceName">Specifies the device name, for example, COM1:.</param>
        /// <param name="deviceState">Indicates the device power state</param>
        /// <returns>Win32 error code</returns>
        /// <remarks>Should be used with extreme care since it may result in an unexpected 
        /// application or system behavior.</remarks>
        public int DevicePowerNotify(string deviceName, DevicePowerStates deviceState)
        {
            uint nError = 0;

            nError = CEDevicePowerNotify(deviceName, (uint)deviceState, (uint)PowerReqFlags.POWER_NAME);

            return (int)nError;
        }

        /// <summary>
        /// Activates notification events. An application can now register to PowerNotify and be 
        /// notified when a power notification is received.
        /// </summary>
        public void EnableNotifications()
        {
            // Set the message queue options
            MessageQueueOptions Options = new MessageQueueOptions();

            // Size in bytes ( 5 * 4)
            Options.Size = (uint)Marshal.SizeOf(Options);
            // Allocate message buffers on demand and to free the message buffers after they are read
            Options.Flags = MSGQUEUE_NOPRECOMMIT;
            // Number of messages in the queue.
            Options.MaxMessages = 32;
            // Number of bytes for each message, do not set to zero.
            Options.MaxMessage = 512;
            // Set to true to request read access to the queue.
            Options.ReadAccess = 1;	// True

            // Create the queue and request power notifications on it
            hMsgQ = CECreateMsgQueue("PowerNotifications", ref Options);

            hReq = CERequestPowerNotifications(hMsgQ, POWER_NOTIFY_ALL);

            // If the above succeed
            if (hMsgQ != IntPtr.Zero && hReq != IntPtr.Zero)
            {
                powerQueue = new Queue();

                // Create an event so that we can kill the thread when we want
                powerThreadAbort = new AutoResetEvent(false);

                // Create the power watcher thread
                new Thread(new ThreadStart(PowerNotifyThread)).Start();
            }
        }

        /// <summary>
        /// Disables power notification events.
        /// </summary>
        public void DisableNotifications()
        {
            // If we are already closed just exit
            if (!powerThreadRunning)
                return;

            // Stop receiving power notifications
            if (hReq != IntPtr.Zero)
                CEStopPowerNotifications(hReq);

            // Attempt to end the PowerNotifyThread
            abortPowerThread = true;
            powerThreadAbort.Set();

            // Wait for the thread to stop
            int count = 0;
            while (powerThreadRunning)
            {
                Thread.Sleep(100);

                // If it did not stop it time record this and give up
                if (count++ > 50)
                    break;
            }
        }

        /// <summary>
        /// Obtain the next PowerInfo structure
        /// </summary>
        public PowerInfo GetNextPowerInfo()
        {
            // Get the next item from the queue in a thread safe manner
            lock (powerQueue.SyncRoot)
                return (PowerInfo)powerQueue.Dequeue();
        }

        /// <summary>
        /// Worker thread that creates and reads a message queue for power notifications
        /// </summary>
        private void PowerNotifyThread()
        {
            powerThreadRunning = true;

            // Keep going util we are asked to quit
            while (!abortPowerThread)
            {
                IntPtr[] Handles = new IntPtr[2];

                Handles[0] = hMsgQ;
                Handles[1] = powerThreadAbort.Handle;

                // Wait on two handles because the message queue will never
                // return from a read unless messages are posted.
                Wait res = (Wait)CEWaitForMultipleObjects(
                                         (uint)Handles.Length,
                                         Handles,
                                         false,
                                         INFINITE);

                // Exit the loop if an abort was requested
                if (abortPowerThread)
                    break;

                // Else
                switch (res)
                {
                    // This must be an error - Exit loop and thread
                    case Wait.Abandoned:
                        abortPowerThread = true;
                        break;

                    // Timeout - Continue after a brief sleep
                    case Wait.Failed:
                        Thread.Sleep(500);
                        break;

                    // Read the message from the queue
                    case Wait.Object:
                        {
                            // Create a new structure to read into
                            PowerInfo Power = new PowerInfo();

                            uint PowerSize = (uint)Marshal.SizeOf(Power);
                            uint BytesRead = 0;
                            uint Flags = 0;

                            // Read the message
                            if (CEReadMsgQueue(hMsgQ, ref Power, PowerSize,
                                                ref BytesRead, 0, ref Flags))
                            {
                                // Set value to zero if percentage is not known
                                if ((Power.BatteryLifePercent < 0) || (Power.BatteryLifePercent > 100))
                                    Power.BatteryLifePercent = 0;

                                if ((Power.BackupBatteryLifePercent < 0) || (Power.BackupBatteryLifePercent > 100))
                                    Power.BackupBatteryLifePercent = 0;

                                // Add the power structure to the queue so that the 
                                // UI thread can get it
                                lock (powerQueue.SyncRoot)
                                    powerQueue.Enqueue(Power);

                                // Fire an event to notify the UI
                                if (PowerNotify != null)
                                    PowerNotify(this, null);
                            }

                            break;
                        }
                }
            }

            // Close the message queue
            if (hMsgQ != IntPtr.Zero)
                CECloseMsgQueue(hMsgQ);

            powerThreadRunning = false;
        }


        #endregion -----------------Methods---------------------

        #region ---------Native Power Management Imports----------

        [DllImport("coredll.dll", EntryPoint = "RequestPowerNotifications")]
        private static extern IntPtr CERequestPowerNotifications(IntPtr hMsgQ, uint Flags);

        [DllImport("coredll.dll", EntryPoint = "StopPowerNotifications")]
        private static extern bool CEStopPowerNotifications(IntPtr hReq);

        [DllImport("coredll.dll", EntryPoint = "SetDevicePower")]
        private static extern uint CESetDevicePower(string Device, uint dwDeviceFlags, uint DeviceState);

        [DllImport("coredll.dll", EntryPoint = "GetDevicePower")]
        private static extern uint CEGetDevicePower(string Device, uint dwDeviceFlags, uint DeviceState);

        [DllImport("coredll.dll", EntryPoint = "DevicePowerNotify")]
        private static extern uint CEDevicePowerNotify(string Device, uint DeviceState, uint Flags);

        [DllImport("coredll.dll", EntryPoint = "SetSystemPowerState")]
        private static extern uint CESetSystemPowerState(IntPtr sState, uint StateFlags, uint Options);

        [DllImport("coredll.dll", EntryPoint = "GetSystemPowerState")]
        private static extern uint CEGetSystemPowerState(StringBuilder Buffer, uint Length, out SystemPowerStates Flags);

        [DllImport("coredll.dll", EntryPoint = "CreateMsgQueue")]
        private static extern IntPtr CECreateMsgQueue(string Name, ref MessageQueueOptions Options);

        [DllImport("coredll.dll", EntryPoint = "CloseMsgQueue")]
        private static extern bool CECloseMsgQueue(IntPtr hMsgQ);

        [DllImport("coredll.dll", EntryPoint = "ReadMsgQueue")]
        private static extern bool CEReadMsgQueue(IntPtr hMsgQ, ref PowerInfo Power, uint BuffSize, ref uint BytesRead, uint Timeout, ref uint Flags);

        [DllImport("coredll.dll", EntryPoint = "WaitForMultipleObjects", SetLastError = true)]
        private static extern int CEWaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, int dwMilliseconds);

        #endregion ---------Native Power Management Imports----------
    }
}

Usage:
Add the above code to your solution or create a DLL file you can add as a reference to multiple solutions. Once it has been added to your solution there is some setup work that you need to do first. Copy and paste the following two methods into your code and call InitializePowerManager(), which will perform all the setup work you need, when your program loads up.

private void InitializePowerManager()
{
     // Create a new instance of the PowerManager class
     PowerManager pwrMngr = new PowerManager();

     // Hook up the Power notify event. This event would not be activated 
     // until EnableNotifications is called. 
     pwrMngr.PowerNotify += new EventHandler(OnPowerNotify);

     // Enable power notifications. This will cause a thread to start
     // that will fire the PowerNotify event when any power notification 
     // is received.
     pwrMngr.EnableNotifications();

     // Get the current power state. 
     StringBuilder systemStateName = new StringBuilder(20);
     PowerManager.SystemPowerStates systemState;
     int nError = pwrMngr.GetSystemPowerState(systemStateName, out systemState);
}

private void OnPowerNotify(object sender, EventArgs e)
{
      // Get the information
      PowerManager.PowerInfo powerInfo = pwrMngr.GetNextPowerInfo();
      int batteryLife = 0;
      // Determine if we are on battery or AC
      if (powerInfo.Message == PowerManager.MessageTypes.Status)
      {
           if (powerInfo.ACLineStatus == PowerManager.ACLineStatus.OnLine)
               powerTypeLabel.Text = "AC: ";
           else
           {
               powerTypeLabel.Text = "Battery: ";
           }
           // Update Battery information
           batteryProgressBar.Value = powerInfo.BatteryLifePercent;
           powerTypeLabel.Text += powerInfo.BatteryLifePercent + "%"; 
      }
}

Manage the devices power state:

//To suspend the Windows CE Device
int nError = pwrMngr.SetSystemPowerState(PowerManager.SystemPowerStates.Suspend);
if (nError != 0)
{
//Error Message
}

Manage the back light

// Notify the backlight to turn off power.
int nError = pwrMngr.DevicePowerNotify("BKL1:", PowerManager.DevicePowerStates.Off);

// Notify the backlight to turn on power.
int nError = pwrMngr.DevicePowerNotify("BKL1:", PowerManager.DevicePowerStates.FullOn);

Monitor the battery
You do not need any special code to monitor the battery as the OnPowerNotify handler that we defined earlier when the power state of your battery changes. In the OnPowerNotify handler you should add code that might notify the user with a pop up message when the battery is too low or it might change an on screen battery meter.

About the Author

Utkarsh Patel is one of our newest members, and he could not have come at a better time. He has helped bring back stability and control to Techlicity Ventures during our rapid growth. If he isn't marvelling at a recently discovered Oracle feature or technique he is bothering the rest of the team on why we should move everything over to Oracle databases. Utkarsh complements the team well with his determination and sound research.

Bookmark and Share
Blog Widget by LinkWithin
blog comments powered by Disqus

Valid XHTML 1.0!