During the development of my little Set Power Scheme daemon, I did a dive into the Power Options API of Windows. Basically I needed a way to programmatically set the active power scheme. The power management functions are located in the PowrProf.dll. Unfortunately there is no managed wrapper to said functionality, but it is a pretty easy API to program against. Specifically we will look at PowerEnumerate, PowerReadFriendlyName, PowerGetActiveScheme and PowerSetActiveScheme.
The first thing one notices is that all power schemes have a unique Guid assigned, which is used to identify the available schemes. So, let’s just get all available Guids. For that we will use PowerEnumerate.
- internal static class WinAPI
- {
- [DllImport("PowrProf.dll")]
- public static extern UInt32 PowerEnumerate(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, UInt32 AcessFlags, UInt32 Index, ref Guid Buffer, ref UInt32 BufferSize);
- public enum AccessFlags : uint
- {
- ACCESS_SCHEME = 16,
- ACCESS_SUBGROUP = 17,
- ACCESS_INDIVIDUAL_SETTING = 18
- }
- }
Calling the function once will yield back the Guid of the power scheme at the specified schemeIndex. So, in order to get all we simply call the function in a while loop until no more schemes are found (= the return value of the function is not 0).
- public IEnumerable<Guid> FindAll()
- {
- var schemeGuid = Guid.Empty;
- uint schemeIndex = 0;
- while (WinAPI.PowerEnumerate(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)WinAPI.AccessFlags.ACCESS_SCHEME, schemeIndex, ref schemeGuid, ref sizeSchemeGuid) == 0)
- {
- yield return schemeGuid;
- schemeIndex++;
- }
- }
Of course only reading the Guid is not very nice, so we will translate the Guid into a friendly name using the PowerReadFriendlyName call.
- [DllImport("PowrProf.dll")]
- public static extern UInt32 PowerReadFriendlyName(IntPtr RootPowerKey, ref Guid SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, IntPtr PowerSettingGuid, IntPtr Buffer, ref UInt32 BufferSize);
This API is not the easiest to use for us that are not using COM Interop on a daily basis. In order to call this API, we have to allocate memory for the friendly name to be written to and when we are done free said memory.
- private static string ReadFriendlyName(Guid schemeGuid)
- {
- uint sizeName = 1024;
- IntPtr pSizeName = Marshal.AllocHGlobal((int)sizeName);
- string friendlyName;
- try
- {
- WinAPI.PowerReadFriendlyName(IntPtr.Zero, ref schemeGuid, IntPtr.Zero, IntPtr.Zero, pSizeName, ref sizeName);
- friendlyName = Marshal.PtrToStringUni(pSizeName);
- }
- finally
- {
- Marshal.FreeHGlobal(pSizeName);
- }
- return friendlyName;
- }
Notice that the free call is executed in a finally block, therefore guaranteeing that it is even called in any exception case.
Finally we can now use the Guid to either get or set the active power scheme.
- [DllImport("PowrProf.dll")]
- public static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);
- [DllImport("PowrProf.dll")]
- public static extern uint PowerSetActiveScheme(IntPtr UserRootPowerKey, ref Guid SchemeGuid);
- public void SetActive(Guid powerSchemeId)
- {
- var schemeGuid = powerSchemeId;
- WinAPI.PowerSetActiveScheme(IntPtr.Zero, ref schemeGuid);
- }
- public Guid GetActive()
- {
- IntPtr pCurrentSchemeGuid = IntPtr.Zero;
- WinAPI.PowerGetActiveScheme(IntPtr.Zero, ref pCurrentSchemeGuid);
- return currentSchemeGuid;
- }
All this functionally is wrapped in a nice repository pattern in Set Power Scheme:
Happy coding.
