Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Determining the Monitor's On/Off (sleep) Status

4.86/5 (19 votes)
25 Jun 2017CPOL2 min read 27.8K   2.3K  
How to determine whether the monitor is on or off as a result of a Windows Power Scheme action

Introduction & Background

This article/project is an extension of Michael Dunn's December 29, 2006 article Vista Goodies in C++: Monitoring the Computer's Power Status Mr. Dunn's article focuses on determining the power source, power scheme, power situation and power status. I had written a desktop wallpaper change program and wanted to be able to determine whether the screen (monitor) was on or in sleep mode so my program would not change the wallpaper when the monitor was off (sleep mode) since that would be pointless. What follows is a modification of Mr. Dunn's approach.

Image 1

Using the Code

Michael Dunn's article focuses on three power setting GUIDs: GUID_POWERSCHEME_PERSONALITY, GUID_ACDC_POWER_SOURCE, and GUID_BATTERY_PERCENTAGE_REMAINING. This project uses only GUID_CONSOLE_DISPLAY_STATE.
 
The full list of Power Setting GUIDs is provided by Microsoft here.

The project is a simple dialog-based MFC Windows application, developed using Visual Studio 2015 Community Edition on a computer running Windows 10 Creator's Update. Although not tested, the code should work with Windows Vista or later. It is also worth noting that I am using this procedure on a desktop computer, not a laptop.

We need to capture the power setting notification messages generated whenever a power setting event, such as turning off the monitor takes place. This is done using the RegisterPowerSettingNotification function. In order to release this captured notification, we must establish a variable that allows us to unregister the notification.

C++
HPOWERNOTIFY m_hScreenStateNotify;

In the OnInitDialog function of the program, we insert the code:

C++
m_hScreenStateNotify = RegisterPowerSettingNotification
                       (this->m_hWnd, &GUID_CONSOLE_DISPLAY_STATE, DEVICE_NOTIFY_WINDOW_HANDLE);

In the OnDestroy function, release the registration:

C++
void CDetectScreenSleepDlg::OnDestroy()
{
    CDialogEx::OnDestroy();

    UnregisterPowerSettingNotification(m_hScreenStateNotify);
}

The meat of the extension, as with Michael Dunn's article, is contained in the message handler function. Click on the main dialog class in the Class View window of VS 2015. Next click on the Messages icon in the Properties window and add the message handler for the WM_POWERBROADCAST message. It has the prototype:

C++
UINT CDetectScreenSleepDlg::OnPowerBroadcast(UINT nPowerEvent, LPARAM nEventData)
The LPARAM nEventData variable contains the information we need to process. It is cast to POWERBROADCAST_SETTING. This structure is defined as:
C++
typedef struct {
       GUID PowerSetting;
       DWORD DataLength;
       UCHAR Data[1];
} POWERBROADCAST_SETTING, *PPOWERBROADCAST_SETTING;

The Data member may have one of the following values:

C++
0x0 - The display is off

0x1 - The display is on.

0x2 - The display is dimmed. Because this program is developed for a desktop machine, 
      the "dimmed" value is returned when the lock screen is displayed prior to the monitor 
      being turned off.

The message handler is:

C++
ON_WM_POWERBROADCAST()

I have used a list control to record the messages sent. We need something to record these messages when the monitor is off (because, of course, we can't see them when the monitor is off :-) ).

C++
UINT CDetectScreenSleepDlg::OnPowerBroadcast(UINT nPowerEvent, LPARAM nEventData)
{
    CString sMsg;
    sMsg.Format(_T("Power Broadcast messages: %d"), m_nPowerMessages);
    POWERBROADCAST_SETTING *pps = (POWERBROADCAST_SETTING*)nEventData;
    if (GUID_CONSOLE_DISPLAY_STATE != pps->PowerSetting)
    {
        // If needed do something if we have a different message rather than the state of the monitor
        return 0;
    }
    UINT uData = pps->Data[0];

    // Do something based on the monitor status. For this demonstration project
    // all I do is record the monitor status when it is on 
    // (when the program is first run and when the system is
    // awakened, uData == 1),
    // when it is dimmed (when the lock-screen is shown, uData == 2), and
    // when the monitor is off (sleep mode, uData == 0)
    int nMax = m_lcEvents.GetItemCount();
    CTime tNow = CTime::GetCurrentTime();
    sMsg.Format(_T("%u"), uData);
    CString sStatus[3] = { _T("Off"), _T("On"), _T("Dimmed") };
    int nItem = m_lcEvents.InsertItem(nMax, sMsg);
    m_lcEvents.SetItemText(nItem, 1, sStatus[uData]);
    m_lcEvents.SetItemText(nItem, 2, tNow.Format("%a %B %d, %y %I:%M:%S %p"));

    return CDialogEx::OnPowerBroadcast(nPowerEvent, nEventData);
}

When I incorporated this into my wallpaper setting program, I set a program variable to TRUE (make the wallpaper changes) when the monitor is ON and to FALSE when the monitor status is either OFF or DIMMED.

Points of Interest

It is worth noting that a POWERBROADCAST message is not posted when a screen saver program is activated. I have also tried monitoring the GUID_SESSION_DISPLAY_STATUS, but I see no difference between it and GUID_CONSOLE_DISPLAY_STATE. If anyone does know how these two GUIDs differ, I would be very interested in the feedback. Of course, I also appreciate all feedback.

History

This is the first presentation of this article (i.e., no history yet).

License

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