I want my sleep key back! (or how to hibernate a machine from your app)

One of the first things I do when I get a new desktop machine or install a new build
of Windows is to map the Sleep button on my keyboard to hibernate instead of
suspend. Hibernate makes more sense for these machines because they will never
run on batteries and I want them to be truly off ;). This worked out great because
I have a bunch of first generation Microsft USB keyboards (they are off white with
a row of blue buttons across the top and a normal extended key layout). I got really,
really used to this. I would just hit the sleep key, the machine would
hibernate and I could walk away. No start menu, no mouse, just a simple key press.

Well, my keyboard at home broke and I bought a Microsoft Natural Ergonomic
Keyboard 4000 to replace it. Works great...but there is no Sleep key on the
keyboard! Ergh. So, instead I figured I would map one of the five
customizable keys (which meant I had to run IntelliType as well... <sigh> )
to an application which would hibernate the machine for me. I thought it would be
obvious, but it wasn't.

In the end, the program is quite short, but figuring out
the magic user mode incantations was hard for a kernel curmudgeon like me.
Anyways, here is the app I wrote to get a machine to hibernate and return my ability to
put hibernate machine with a single keypress. All it does is adjust the
process's token to enable SE_SHUTDOWN_NAME and then call SetSystemPowerState().
Simple as it is, I am proud of it ;).

 
    #ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
    #endif //  WIN32_LEAN_AND_MEAN

    #include <windows.h>
    #include <stdio.h>

    BOOL
    SetPrivilege(
        HANDLE hToken,          // access token handle
        LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
        BOOL bEnablePrivilege   // to enable or disable privilege
        )
    {
        TOKEN_PRIVILEGES tp;
        LUID luid;

        if (!LookupPrivilegeValue(NULL,            // lookup privilege on local system
                                  lpszPrivilege,   // privilege to lookup
                                  &luid)) {        // receives LUID of privilege
            printf("LookupPrivilegeValue error: %u\n", GetLastError() );
            return FALSE;
        }

        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = luid;
        if (bEnablePrivilege) {
            tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        }
        else {
            tp.Privileges[0].Attributes = 0;
        }

        //
        // Enable the privilege or disable all privileges.
        //
        if (!AdjustTokenPrivileges(hToken,
                                   FALSE,
                                   &tp,
                                   sizeof(TOKEN_PRIVILEGES),
                                   (PTOKEN_PRIVILEGES) NULL,
                                   (PDWORD) NULL)) {
            printf("AdjustTokenPrivileges error: %d / 0x%x\n",
                   GetLastError(), GetLastError() );
            return FALSE;
        }

        if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
            printf("The token does not have the specified privilege.\n");
            return FALSE;
        }

        return TRUE;
    }

    int __cdecl main()
    {
        HANDLE hToken;

        if (!OpenProcessToken(GetCurrentProcess(),
                              TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                              &hToken))  {
            printf("open failed %d / 0x%x\n", GetLastError(), GetLastError());
            return FALSE;
        }

        if (SetPrivilege(hToken,
                         SE_SHUTDOWN_NAME,
                         TRUE)) {
            if (SetSystemPowerState(FALSE, TRUE) == FALSE) {
                printf("failed %d / 0x%x\n", GetLastError(), GetLastError());
            }
        }
        else {
            printf("set failed %d / 0x%x\n", GetLastError(), GetLastError());
        }

        return 0;
    }

Comments

  • Anonymous
    August 28, 2006
    try "shutdown.exe /h" next time

    I guess you can put this down as 'professional development' though. :-)

  • Anonymous
    August 28, 2006
    I forgot to mention that I tried that already.  The /h option for shutdown.exe was added post XP SP2, which is the OS I really needed this for ;).

  • Anonymous
    August 28, 2006
    Can this piece od code be the possible solution for  this [ http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_frm/thread/805ac94f863ef9c6/# ] problem?



    ali

  • Anonymous
    August 28, 2006
    No, it doesn't solve that problem. in the thread, the calleer wants to disable a keyboard sleep button (see my response), here i am replacing a missing button

    d

  • Anonymous
    August 28, 2006
    For this, I use Cygwin's hibernate program; any version of Windows that can hibernate, it'll hibernate.

  • Anonymous
    August 29, 2006
    well, that requires a toolchain that I don't want to download ;).  besides, now i can do fun stuff like set a timer to hiber in 30 minutes and then create a waitable timer to wake the machine up later ;0.

    d

  • Anonymous
    September 09, 2006
    The comment has been removed

  • Anonymous
    September 11, 2006
    Moso, good question on SetSuspendState().   I didn't know about this function until you asked.  It does all of this work for your actually.  It will enable the SE_SHUTDOWN_NAME privilege for you before attempting the system power state transition.  Definitely makes for a simpler application :).

    d

  • Anonymous
    November 30, 2006
    I am a creature of habit. Once I get into a pattern, especially with keyboard keys (i.e. my sleep key

  • Anonymous
    May 30, 2007
    Sorry for bringing this old thread up, but doronh, can you tell me how you create that waitable timer to wake the machine up later? -jani

  • Anonymous
    June 14, 2009
    PingBack from http://adirondackchairshub.info/story.php?id=2173