jmazner

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

Download this episode

Download Video

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

Format

Available formats for this video:

Actual format may change based on video formats available and browser capability.

    The Discussion

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

    Comments closed

    Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.