none
How to change Power Button Policy in Vista? RRS feed

  • Question

  • Hi,

    I am (unsuccessfully) trying to change the policy of the Power button in Vista.

    The flow:
    (1) Get the active scheme using 'PowerGetActiveScheme'.
    (2) Change power setting for the power button (GUID 7648efa3-dd9c-4e3e-b566-50f929386280) using 'PowerWriteACValueIndex' and 'PowerWriteDCValueIndex'.
    (3) Set PBUTTONACTION (GUID 7648efa3-dd9c-4e3e-b566-50f929386280) = 0 (Do Nothing), 3 (Shutdown), 1 (Sleep), 2 (Hibernate).
    (4) Activate the power scheme using 'PowerSetActiveScheme'.

    The code:

    [DllImport("powrprof.dll", EntryPoint = "PowerGetActiveScheme")] 
    public static extern UInt32 PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr SchemeGuid); 
     
    [DllImport("powrprof.dll", EntryPoint = "PowerWriteACValueIndex")] 
    public static extern uint PowerWriteACValueIndex([InAttribute()] IntPtr RootPowerKey, 
                [InAttribute()] IntPtr SchemeGuid, 
                [InAttribute()] IntPtr SubGroupOfPowerSettingsGuid, 
                [InAttribute()] IntPtr PowerSettingGuid, 
                uint AcValueIndex); 
     
    [DllImport("powrprof.dll", EntryPoint = "PowerWriteDCValueIndex")] 
    public static extern uint PowerWriteDCValueIndex([InAttribute()] IntPtr RootPowerKey, 
                [InAttribute()] IntPtr SchemeGuid, 
                [InAttribute()] IntPtr SubGroupOfPowerSettingsGuid, 
                [InAttribute()] IntPtr PowerSettingGuid, 
                uint DcValueIndex); 
     
    [DllImport("powrprof.dll", EntryPoint = "PowerSetActiveScheme")] 
    public static extern uint PowerSetActiveScheme([InAttribute()] IntPtr UserRootPowerKey, 
                [InAttribute()] IntPtr SchemeGuid); 
     
    public bool ChangePWRButtonPolicy(uint ValueIndex) 
        IntPtr ptrActiveGuid = IntPtr.Zero; 
     
        IntPtr ptrPBUTTONACTION = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); 
        IntPtr ptrGUID_SLEEP_SUBGROUP = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); 
     
        Guid PBUTTONACTION = new Guid(0x7648efa3, 0xdd9c, 0x4e3e, 0xb5, 0x66, 0x50, 0xf9, 0x29, 0x38, 0x62, 0x80); 
        Guid GUID_SLEEP_SUBGROUP = new Guid(0x238C9FA8, 0x0AAD, 0x41ED, 0x83, 0xF4, 0x97, 0xBE, 0x24, 0x2C, 0x8F, 0x20); 
     
        Marshal.StructureToPtr(PBUTTONACTION, ptrPBUTTONACTION, true); 
        Marshal.StructureToPtr(GUID_SLEEP_SUBGROUP, ptrGUID_SLEEP_SUBGROUP, true); 
     
        uint result; 
     
        result = PowerGetActiveScheme(IntPtr.Zero, ref ptrActiveGuid); 
        if (result == 0) 
        { 
            result = PowerWriteACValueIndex(IntPtr.Zero, ptrActiveGuid, ptrGUID_SLEEP_SUBGROUP, ptrPBUTTONACTION, ValueIndex); 
            if (result == 0) 
            { 
                result = PowerWriteDCValueIndex(IntPtr.Zero, ptrActiveGuid, ptrGUID_SLEEP_SUBGROUP, ptrPBUTTONACTION, ValueIndex); 
                if (result == 0) 
                { 
                    result = PowerSetActiveScheme(IntPtr.Zero, ptrActiveGuid); 
                    if (result == 0) 
                        return true; 
                } 
            } 
        } 
        return false; 



    'PowerWriteACValueIndex' fails to write and returns 2.
    Are there wrong things I am doing? (probably so :) )
    Thanks, Homer.

    Tuesday, December 16, 2008 7:28 AM

All replies

  • I don't see you declare nor assign the SubGroupOfPowerSettingsGuid variable.  Pass this argument as ref Guid so you have a fighting chance to properly set the value.
    Hans Passant.
    Tuesday, December 16, 2008 12:54 PM
    Moderator
  • I'm sorry it was a typo.  SubGroupOfPowerSettingsGuid is actually the ptrGUID_SLEEP_SUBGROUP pointer. 

    Tuesday, December 16, 2008 4:42 PM
  • Well, that was a pretty big typo.  So, what value does the function actually return?
    Hans Passant.
    Wednesday, December 17, 2008 12:57 PM
    Moderator
  • In your declaration of PowerWriteACValueIndex, the SchemeGuid parameter should be passed by value if declared as an IntPtr (ie remove the ref modifier).
    Mattias, C# MVP
    Thursday, December 18, 2008 8:46 AM
    Moderator
  • I've changed it so SchemeGuid parameter in PowerWriteACValueIndex function passed by value,

    It did not help. PowerWriteACValueIndex function still returns 2.

    Please advise.

    Friday, December 19, 2008 8:22 AM
  •  Whoever understands this well enough, please explain why it does not work. 
    Wednesday, December 31, 2008 10:47 AM
  • Do you know what "2" means? This error value (ERROR_FILE_NOT_FOUND) is often used by the Win32 API to indicate that something wasn't "found".

    In this particular case of the power API, it means that in the SLEEP_SUBGROUP, the POWERBUTTON_ACTION wasn't found. (Think of "subgroups" as folders).

    A brief review of the header file will reveal why: POWERBUTTON_ACTION is a member of the SYSTEM_BUTTON_SUBGROUP (0x4F971E89, 0xEEBD, 0x4455, 0xA8, 0xDE, 0x9E, 0x59, 0x04, 0x0E, 0x73, 0x47). Give that a try and see if it works.

            -Steve

    P.S. One of the first things when doing interop should be to handle error conditions, and create "safe" wrappers for the imports. There's a well-known procedure for translating Win32 errors into HRESULTS, and then you can use Marshal.ThrowExceptionForHR. This will give you an exception with a reasonable error message when you do run into problems.
    Wednesday, December 31, 2008 2:10 PM
  •  

    Thanks Steve!

     

    Indeed, the POWERBUTTON_ACTION is a part of SYSTEM_BUTTON_SUBGROUP, my fault.

    After substituting SLEEP_SUBGROUP with SYSTEM_BUTTON_SUBGROUP, both functions (PowerWriteACValueIndex and PowerWriteDCValueIndex) return ERROR_SUCCESS.

    But the power policy doesn’t change!

    I set manually the value ’Do nothing’ for ‘When I press the power button:’ option, then I run the code above where ‘1’ is set for the ValueIndex (which means sleep power button action) and then check in Power Options – “When I press the power button” the value did not change from ‘Do nothing’.

     

    Is it something wrong with the flow?

     

    P.S.

    Thanks for the great tip regarding the errors handling. I am definitely going to use it.

     

    Somer.

    Sunday, January 4, 2009 7:45 AM
  • I'm not that familiar with the power APIs, but (after checking the return value of PowerSetActiveSceme), check to see if it takes effect after a reboot. Have you read Application Power Management Best Practices for Windows Vista?

           -Steve
    Monday, January 5, 2009 10:48 AM