Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VC9.0

Tunr off monitor when machine gets locked

3.63/5 (6 votes)
31 Oct 2008CPOL3 min read 19.2K   311  
An utility to turn off monitor as soon as we lock the machine (Windows).

Introduction

A couple of months back, the “Social Activity Group” in my company started a campaign whereby it would turn off the monitors if seen not in use. This was really a great initiative to bring down the energy wastage within the company. This motivated me to write a simple application that would do what the group was trying. The best time when a monitor should turn off is when we lock the computer. Hence it makes sense to turn it off as soon as we lock a machine.

The Solution

The solution is simple. Just follow the steps below:

  1. Capture the “Windows Lock/Unclock” event.
  2. Capture User Interaction events like Mouse Move, Keyboard hit, so that if monitor gets turned on because of these movements, it could again be turned off.
  3. Signal the monitor to turn off if the computer is locked.
  4. Repeat steps 2 and 3 until the machine gets unlocked.

Step 1: Capture the Windows Lock/Unlock event

There are several ways to get an event for Windows Lock and Unlock, of which I chose to go ahead with the Winlogon Notification Package. The Winlogon Notification Package is a DLL which lets you export functions to handle Winlogon.exe events. These event messages includes lock, unlock, logoff, logon, startup, shutdown, startscreensaver, stopscreensaver, and startshell etc.

All you have to do is write a DLL exporting the event handlers and make a few Registry entries. Below is the code snippet from the attached project which has three event handles: “CompLocked”, “CompUnlocked”, and “CompLoggedIn” exported from “ShutDownMonitor.dll” (attached with the code).

C++
HANDLE g_hUnLockEvent           = NULL;//An event handle to signal System 
                                       //Unlock Event
HANDLE g_hMovementEvent         = NULL;//An event handle to signal 
                                       //any mouse movement/keboard hit event 
VOID CompLocked( PWLX_NOTIFICATION_INFO pInfo )
{
    //When the computer gets locked, create two event objects to signal 
    //Unlock event and Keyboard/Mouse movement event
    if( !g_hUnLockEvent )
    {
        //First lock event
        g_hUnLockEvent = CreateEvent( NULL, TRUE, FALSE, _T("MonitorShutDown") );
    }
    else
    {
        ResetEvent( g_hUnLockEvent );
    }   

    if( !g_hMovementEvent )
    {
        //First lock event
        g_hMovementEvent = CreateEvent(NULL, TRUE, TRUE, _T("MovementCaptured"));
    }
    else
    {
        SetEvent( g_hMovementEvent );
    }

    // Create a thread "ShutDownMonitor" to monitor systemt lock/unlock state, 
    // Implementation given in step 3
    CreateThread( NULL, 0, ShutDownMonitor, NULL, 0, NULL );    
}

VOID CompUnlocked( PWLX_NOTIFICATION_INFO pInfo )
{
    if( g_hUnLockEvent )
    {
        SetEvent( g_hUnLockEvent );
    }    
}
 
VOID CompLoggedIn( PWLX_NOTIFICATION_INFO pInfo )
{
    if( g_hUnLockEvent )
    {
        SetEvent( g_hUnLockEvent );
    }    
}

Step 2: Capture User Interaction events like Mouse Move, Keyboard hit

This can be easily achieved using SetWindowsHookEx. You will have to set a hook for WH_MOUSE and WH_KEYBOARD to capture mouse and keyboard events. The calls would look like:

C++
SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, hModule, 0 );
SetWindowsHookEx( WH_MOUSE, MouseProc, hModule, 0 );
  • KeyboardProc: The hook procedure for keyboard events.
  • MouseProc: The hook procedure for mouse events.
  • hModule: Handle of the module that implements KeyboardProc and MouseProc.

I implemented these functions right in my Winlogon Notification Package, i.e., “ShutDownMonitor.dll”, and the implementation looks like:

C++
LRESULT CALLBACK MouseProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    //One weird thing that I observed was, even when i had my systemt left idle,
    //My opticle mouse was resulting into a minor displacemets like 3-4 pixels
    //So, decided to set the event only if the displacement is considerable
    
    static POINT ptOld = reinterpret_cast<PMOUSEHOOKSTRUCT>( lParam )->pt;
    POINT ptNew = reinterpret_cast<PMOUSEHOOKSTRUCT>( lParam )->pt;

    //Get the displacement of the mouse position in pixel
    int nXDisp = abs( ptNew.x - ptOld.x );
    int nYDisp = abs( ptNew.y - ptOld.y );
    
    if( ( WAIT_OBJECT_0 != WaitForSingleObject( g_hMovementEvent, 0 ) )  &&
        ( nXDisp || nYDisp ) )
    {
        EnterCriticalSection( &g_csMovement );
        SetEvent( g_hMovementEvent );
        LeaveCriticalSection( &g_csMovement );
    }
    
    //Save the current position of the mouse
    ptOld = ptNew;

    return CallNextHookEx( g_hMouseHook, nCode, wParam, lParam );
}

LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
    if( WAIT_OBJECT_0 != WaitForSingleObject( g_hMovementEvent, 0 ) )
    {
        //Set the event only when the key is being released(31st bit 1).
        if( (lParam & 0x80000000) && (lParam & 0x40000000) )
        {
            EnterCriticalSection( &g_csMovement );
            SetEvent( g_hMovementEvent );
            LeaveCriticalSection( &g_csMovement );
        }
    }
    
    return CallNextHookEx( g_hKBHook, nCode, wParam, lParam ); 
}

Step 3: Signal the monitor to turn off if the computer is locked

Turning the monitor on/off can be done using SendMessage():

C++
const int ON = -1;
const int OFF = 2;
const int STANBY = 1;

SendMessage( hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, OFF);

where hWnd is the handle to the topmost window. When the computer is locked, the topmost window has the caption “Computer Locked”. So, our task is to find this window and send it a message to power off the monitor. Thus, the “ShutDownMonitor” thread used in step 1 goes like this:

C++
DWORD WINAPI ShutDownMonitor( LPVOID )
{
    DWORD dwDisposition = 0;
    HKEY hKey = NULL;
    DWORD dwSleepTime = 10;
    
    InitializeCriticalSection( &g_csMovement );

    //Set the mouse and keyboard hooks
    HMODULE hModule = LoadLibrary( _T("ShutDownMonitor.dll") );

    typedef LRESULT  (CALLBACK *HOOKPROC)( int nCode, WPARAM wParam, LPARAM lParam );
    HOOKPROC MouseProc = NULL;
    HOOKPROC KeyboardProc = NULL;
    if( hModule )
    {
        MouseProc = (HOOKPROC)GetProcAddress( hModule, "MouseProc" ); 
        KeyboardProc = (HOOKPROC)GetProcAddress( hModule, "KeyboardProc" );
    }
    if( MouseProc )
    {
        g_hMouseHook = ::SetWindowsHookEx( WH_MOUSE, MouseProc, hModule, 0 );
    }
    if( KeyboardProc )
    {
        g_hKBHook = ::SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, hModule, 0 );
    }
 
    HWND hWnd = NULL;
    while( WAIT_TIMEOUT == WaitForSingleObject( g_hUnLockEvent, 500 ) )
    {
        hWnd = FindWindow( NULL, _T("Computer Locked") );
        if( hWnd )
        {
            break;
        }
    }

    do
    {
        hWnd = FindWindow( NULL, _T("Computer Locked") );
        if( hWnd )
        {
            RECT rectWnd = {0};
            GetWindowRect( hWnd, &rectWnd );

            int x = rectWnd.left + ( rectWnd.right - rectWnd.left )/2;
            int y = rectWnd.top + ( rectWnd.bottom - rectWnd.top )/2;
            SetCursorPos( x, y );

            //This will put the monitor in sleep mode
            ::SendMessage( hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, OFF );
        }      
                
        EnterCriticalSection( &g_csMovement );
        ResetEvent( g_hMovementEvent );        
        LeaveCriticalSection( &g_csMovement );

        WaitForSingleObject( g_hMovementEvent, INFINITE );   
        
        //This is just to take care that MONITOR POWWER OFF messages 
        //are not sent very frequently
        //One can use his/her own time out values
        if( dwSleepTime < 60 )
        {
            dwSleepTime *= 2;
        }    
        
    }while( WAIT_TIMEOUT == WaitForSingleObject( g_hUnLockEvent, dwSleepTime*1000 ) );

    //Release the hooks
    if( g_hKBHook )
    {
        UnhookWindowsHookEx( g_hKBHook );
        g_hKBHook = NULL;
    }
    if( g_hMouseHook )
    {
        UnhookWindowsHookEx( g_hMouseHook );
        g_hMouseHook = NULL;
    }
    
    DeleteCriticalSection( &g_csMovement );
    
    return 0;
}

Installing the utility

In order to have this utility installed on the machine, copy “ShutDownMonitor.dll” into c:\windows\System32 and make the following Registry changes:

HKEY_LOCAL_MACHINE
  \Software
     \Microsoft
        \Windows NT
           \CurrentVersion
              \Winlogon
                 \Notify
                    \ShutDownMonitor
                        \Asynchronous  REG_DWORD 1
                         \DllName      REG_SZ    ShutDownMonitor.dll
                         \Lock         REG_SZ    CompLocked
                         \Logon        REG_SZ    CompLoggedIn
                         \Unlock       REG_SZ    CompUnlocked

After the Registry changes are done, you will have to restart the machine to get the utility into effect. That’s it, you are all set to save precious energy :)

And, if you want to uninstall the utility, first delete the Registry changes, restart the machine, and then delete the file “ShutDownMonitor.dll” from c:\windows\system32.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)