65.9K
CodeProject 正在变化。 阅读更多。
Home

计算机锁定时关闭显示器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.63/5 (6投票s)

2008 年 11 月 1 日

CPOL

3分钟阅读

viewsIcon

19472

downloadIcon

326

一个实用工具,用于在锁定机器(Windows)时立即关闭显示器。

引言

几个月前,我公司的“社会活动小组”发起了一项活动,如果发现显示器未使用,则将其关闭。这确实是一项减少公司能源浪费的伟大举措。这激励我编写一个简单的应用程序来完成该小组正在尝试做的事情。关闭显示器的最佳时机是我们锁定计算机的时候。因此,锁定机器后立即将其关闭是有意义的。

解决方案

解决方案很简单。只需按照以下步骤操作

  1. 捕获“Windows 锁定/解锁”事件。
  2. 捕获用户交互事件,如鼠标移动、键盘敲击,以便如果由于这些移动导致显示器打开,则可以再次将其关闭。
  3. 如果计算机已锁定,则发出信号以关闭显示器。
  4. 重复步骤 2 和 3,直到机器解锁。

步骤 1:捕获 Windows 锁定/解锁事件

有几种方法可以获取 Windows 锁定和解锁的事件,我选择使用 Winlogon 通知包。 Winlogon 通知包是一个 DLL,允许您导出函数来处理 Winlogon.exe 事件。 这些事件消息包括锁定、解锁、注销、登录、启动、关闭、启动屏幕保护程序、停止屏幕保护程序和启动 shell 等。

您所要做的就是编写一个 DLL 导出事件处理程序并进行一些注册表项。 以下是附加项目中的代码片段,该项目具有三个事件句柄:“CompLocked”、“CompUnlocked”和“CompLoggedIn”,这些句柄从“ShutDownMonitor.dll”(随代码一起提供)导出。

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

步骤 2:捕获用户交互事件,如鼠标移动、键盘敲击

这可以使用 SetWindowsHookEx 轻松实现。 您必须为 WH_MOUSEWH_KEYBOARD 设置一个钩子来捕获鼠标和键盘事件。 调用看起来像

SetWindowsHookEx( WH_KEYBOARD, KeyboardProc, hModule, 0 );
SetWindowsHookEx( WH_MOUSE, MouseProc, hModule, 0 );
  • KeyboardProc:键盘事件的钩子程序。
  • MouseProc:鼠标事件的钩子程序。
  • hModule:实现 KeyboardProcMouseProc 的模块的句柄。

我在我的 Winlogon 通知包中实现了这些函数,即“ShutDownMonitor.dll”,实现如下所示

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

步骤 3:如果计算机已锁定,则发出信号以关闭显示器

可以使用 SendMessage() 打开/关闭显示器

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

SendMessage( hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, OFF);

其中 hWnd 是最顶层窗口的句柄。当计算机锁定时,最顶层窗口的标题为“计算机已锁定”。因此,我们的任务是找到此窗口并向其发送一条消息以关闭显示器。因此,步骤 1 中使用的“ShutDownMonitor”线程如下所示

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

安装实用程序

为了在机器上安装此实用程序,请将“ShutDownMonitor.dll”复制到 c:\windows\System32 中并进行以下注册表更改

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

完成注册表更改后,您需要重新启动机器才能使该实用程序生效。 就是这样,您已经准备好节省宝贵的能源了:)

并且,如果您想卸载该实用程序,请首先删除注册表更改,重新启动机器,然后从 c:\windows\system32 中删除文件“ShutDownMonitor.dll”。

参考文献

© . All rights reserved.