Knowing the current power source is a good start, but your application then needs to be alerted when the power source changes. It is also important to be alerted when the computer is going to suspend or hibernate and when it wakes up or restores its state after a suspend or hibernate.
In the .NET Framework 1.1, there is a PowerModeChanged
event in the SystemEvents class in the Microsoft.Win32 namespace. The PowerModeChangedEvent
passes you a PowerModeChangedEventArgs object containing a Mode
property that is a PowerModes enumerated type. The possible PowerModes you might receive are Resume
, and Suspend
It is obvious what the Resume
events indicate, but the StatusChange
event can indicate any change in the current state of the power. The changes that might be indicated include a switch from AC to DC, a switch from DC to AC, or a change in the state of the battery (for example, indicating that the battery has only a low charge remaining).
When you receive a PowerModeChanged
event with the StatusChange
flag, it would be good practice to call the GetSystemPowerStatus
method discussed earlier to find out what specifically has changed.
event provides a subset of information that can be received from the WM_POWERBROADCAST
Windows message. This whitepaper on MSDN
discusses how to use the WM_POWERBROADCAST
message, but for most applications, using the PowerModeChanged
event should be sufficient.
Being Power Efficient
The user experience on a Mobile PC can be greatly improved by making sure your software does not consume more battery power than is needed. You can monitor this by carefully considering the requirements of your application when it runs on a computer-powered by battery.
Some operations, such as audio output or graphics-intensive operations, are obvious power gluttons. Less obvious suspects are the impact that use of the hard disk and network have on battery life. In fact, all CPU operations have some impact on battery life and if your application is making excessive use of polling or background threads, these will also degrade performance of the battery.
Often, these are not purely technical decisions; the business decision makers need to be involved to understand the impact of requested features on battery life. It is good practice to avoid carrying out non-critical tasks on a computer that is currently powered only by battery.
Suspension of Operations
The consideration so far has been how to reduce power consumption when the computer is powered by batteries. There is one other important consideration to take into account: how your application should behave when the computer suspends. Suspending the computer is the primary way Windows helps extend mobile PC battery life when the computer is not in use.
|To the end user, the resume experience should be seamless; it should appear as if the application is simply running again.|
There are two scenarios to consider that will cause the computer to go into suspend mode. The first is when the computer has been idle for a set time period and the Power Options are set for the PC to stand by or hibernate after that period of inactivity. The second scenario is when the user shuts the lid or pushes the power button, which causes a request to suspend operations.
When the operating system suspends, it sends out the suspend notification message, which you can receive in the PowerModeChanged
event. Your application then has to stop any limited cleanup actions and store any important state data as quickly as possible.
You should never request any user input on suspend. There is nothing more annoying to a user who closes the laptop's lid to find that the computer did not suspend because your application was waiting for a response from a MessageBox and the battery has drained.
Once you have worked out how your application responds to a suspend event, you should consider how it behaves upon receiving a resume notification. To the end user, the resume experience should be seamless; it should appear as if the application is simply running again.
The devices attached to the computer when it suspends may have changed by the time the computer resumes operations. The computer may have been docked, undocked, had other peripherals changed or have a different network connectivity. Your application has to take account of this change and act accordingly, without interrupting the user or using a popup dialog box, if at all possible.
|Your application must never veto a suspend request.|
Your application must never veto a suspend request. Once the computer has sent a notification to suspend, your application must comply. This becomes even more important in Windows Vista, where your application will be given a maximum of two seconds to respond to a suspend notification before the operating system suspends, and vetoing a second request is not possible.
Although your application cannot do anything once the suspend request has been sent out, you may wish to prevent the computer from requesting a suspend request based on idle time.
For example, if your application is playing a video in a window, it is likely that while the user is watching that video, they will not be interacting with the computer. After a certain period of inactivity, the idle timer requests that the computer suspend. You can prevent the idle timer from triggering a request to suspend by using the Win32 SetThreadExecutionState
API. You should only do this when it is absolutely necessary, as it will cause degradation in battery life.
As the name suggests, SetThreadExecustionState
is per thread. The operating system maintains a count of threads that have requested a certain state, like perhaps the display is required. Only when each of these threads has indicated that they no longer require the resource will the resource be allowed to suspend. SetThreadExecutionState
does not prevent the system from suspending when it is user-initiated.
function and the EXECUTION_STATE
types can be defined in C# as shown in the code snippet below:
public enum EXECUTION_STATE :uint
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002,
// legacy flag should not be used
// ES_USER_PRESENT = 0x00000004,
ES_CONTINUOUS = 0x80000000,
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
private extern static