How To: Use Vista's Power Management APIs to Be A Good Laptop Citizen

Sign in to queue

Description

Is your application the reason why my laptop only gets two hours of battery life?  That's not a question you want to hear from your paying customers.  Windows Vista provides an enhanced power management API and control panel which lets the user express their desired power usage profile, and lets applications query and respond to changes in power state.

In the second of our Vista How To series, Ian Griffiths shows how to listen to power change events in a WPF app, and respond to low power or power saver modes by reducing the amount of animation in the app (thus saving GPU and CPU cycles)

Embed

Download

Download this episode

The Discussion

  • User profile image
    jmazner

    Here are some relevant sections of the code from Ian's sample app.  I've added the interop definitions to PInvoke.net for easy reference.

      void Window1_Loaded(object sender, RoutedEventArgs e)
            {
                source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                source.AddHook(new HwndSourceHook(WndProc));
                RegisterForPowerNotifications(source.Handle);
            }
      protected override void OnClosed(EventArgs e)
            {
                base.OnClosed(e);
                UnregisterForPowerNotifications();
            }

    private IntPtr hBattCapacity;
            private IntPtr hMonitorOn;
            private IntPtr hPowerScheme;
            private IntPtr hPowerSrc;

            private void RegisterForPowerNotifications(IntPtr hwnd)
            {
                hPowerSrc = RegisterPowerSettingNotification(hwnd,
                ref GUID_ACDC_POWER_SOURCE,
                DEVICE_NOTIFY_WINDOW_HANDLE);

                hBattCapacity = RegisterPowerSettingNotification(hwnd,
                ref GUID_BATTERY_PERCENTAGE_REMAINING,
                DEVICE_NOTIFY_WINDOW_HANDLE);

                hMonitorOn = RegisterPowerSettingNotification(hwnd,
                ref GUID_MONITOR_POWER_ON,
                DEVICE_NOTIFY_WINDOW_HANDLE);

                hPowerScheme = RegisterPowerSettingNotification(hwnd,
                ref GUID_POWERSCHEME_PERSONALITY,
                DEVICE_NOTIFY_WINDOW_HANDLE);
            }

            private void UnregisterForPowerNotifications()
            {
                UnregisterPowerSettingNotification(hBattCapacity);
                UnregisterPowerSettingNotification(hMonitorOn);
                UnregisterPowerSettingNotification(hPowerScheme);
                UnregisterPowerSettingNotification(hPowerSrc);
            }

            static bool onBattery;
            enum PowerPersonality { Savings, Performance, Mixed };
            static PowerPersonality personality;
            private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                if (msg == WM_POWERBROADCAST && wParam.ToInt32() == PBT_POWERSETTINGCHANGE)
                {
                    // Extract data from message
                    POWERBROADCAST_SETTING ps =
                     (POWERBROADCAST_SETTING)Marshal.PtrToStructure(
                         lParam, typeof(POWERBROADCAST_SETTING));
                    IntPtr pData = (IntPtr)(lParam.ToInt32() + Marshal.SizeOf(ps));


                    // Examine notification

                    if (ps.PowerSetting == GUID_POWERSCHEME_PERSONALITY &&
                        ps.DataLength == Marshal.SizeOf(typeof(Guid)))
                    {
                        // New power scheme selected.

                        Guid newPersonality =
                            (Guid)Marshal.PtrToStructure(pData, typeof(Guid));

                        if (newPersonality == GUID_MAX_POWER_SAVINGS)
                        {
                            personality = PowerPersonality.Savings;
                        }
                        else if (newPersonality == GUID_MIN_POWER_SAVINGS)
                        {
                            personality = PowerPersonality.Performance;
                        }
                        else if (newPersonality == GUID_TYPICAL_POWER_SAVINGS)
                        {
                            personality = PowerPersonality.Mixed;
                        }
                        else
                        {
                            Debug.WriteLine("switched to unknown Power savings");
                            personality = PowerPersonality.Mixed;
                        }
                    }
                    else if (ps.PowerSetting == GUID_ACDC_POWER_SOURCE &&
                             ps.DataLength == Marshal.SizeOf(typeof(Int32)))
                    {
                        Int32 iData = (Int32)Marshal.PtrToStructure(pData, typeof(Int32));
                        Debug.WriteLine("ACDC: " + iData);

                        onBattery = iData != 0;
                    }


                    // Select the best resources for the current
                    // power state.

                    if (personality == PowerPersonality.Performance
                        || (personality == PowerPersonality.Mixed && !onBattery))
                    {
                        Application.Current.Resources = fullPowerResources;
                    }
                    else
                    {
                        Application.Current.Resources = lowPowerResources;
                    }

                }
               
                return IntPtr.Zero;
            }

Add Your 2 Cents