C++: check if computer is locked
Asked Answered
D

5

5

I'm trying to figure out whether the computer is locked.

I've looked at LockWorkStation function, but the function that I'm hoping to find is IsWorkStationLocked.


I need to support all windows version >= XP

Disorientate answered 29/3, 2015 at 7:26 Comment(4)
And you want to check is computer was locked while program running or if the computer is locked when program starts executing (like schedule)? For the first one you can use OnSessionChange event.Susansusana
While to program is runningDisorientate
Check this related question on C#.Susansusana
Someone just posted code that does this: #29350513Ducan
D
5

From the same MSDN link you gave, the third paragraph of "Remarks" says:

This function has the same result as pressing Ctrl+Alt+Del and clicking Lock Workstation. To unlock the workstation, the user must log in. There is no function you can call to determine whether the workstation is locked. To receive notification when the user logs in, use the WTSRegisterSessionNotification function to receive WM_WTSSESSION_CHANGE messages. You can use session notifications to track the desktop state so you know whether it is possible to interact with the user.

Damnatory answered 29/3, 2015 at 7:31 Comment(0)
M
11

For windows 7 and abowe WTS API can be used:

bool IsSessionLocked() {
    typedef BOOL (PASCAL * WTSQuerySessionInformation)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned);
    typedef void (PASCAL * WTSFreeMemory)( PVOID pMemory);

    WTSINFOEXW * pInfo = NULL;
    WTS_INFO_CLASS wtsic = DW_WTSSessionInfoEx;
    bool bRet = false;
    LPTSTR ppBuffer = NULL;
    DWORD dwBytesReturned = 0;
    LONG dwFlags = 0;
    WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
    WTSFreeMemory pWTSFreeMemory = NULL;

    HMODULE hLib = LoadLibrary( _T("wtsapi32.dll") );
    if (!hLib) {
        return false;
    }
    pWTSQuerySessionInformation = (WTSQuerySessionInformation)GetProcAddress(hLib, "WTSQuerySessionInformationW" );
    if (!pWTSQuerySessionInformation) {
        goto EXIT;
    }

    pWTSFreeMemory = (WTSFreeMemory)GetProcAddress(hLib, "WTSFreeMemory" );
    if (pWTSFreeMemory == NULL) {
        goto EXIT;
    }

    if(pWTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, g_dwSessionID, wtsic, &ppBuffer, &dwBytesReturned)) {
        if(dwBytesReturned > 0) {
            pInfo =  (WTSINFOEXW*)ppBuffer; 
            if (pInfo->Level == 1) {
                dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
            }
            if (dwFlags == WTS_SESSIONSTATE_LOCK) {
                bRet = true;
            }
        }
        pWTSFreeMemory(ppBuffer);
        ppBuffer = NULL;
    }
EXIT:
    if (hLib != NULL) {
        FreeLibrary(hLib);
    }
    return bRet;
}

Please check following article for supported platforms for WTSINFOEX structure: https://technet.microsoft.com/ru-ru/sysinternals/ee621017

Meagher answered 20/8, 2015 at 10:24 Comment(1)
Where is the definition for DW_WTSSessionInfoEx ?Ph
D
5

From the same MSDN link you gave, the third paragraph of "Remarks" says:

This function has the same result as pressing Ctrl+Alt+Del and clicking Lock Workstation. To unlock the workstation, the user must log in. There is no function you can call to determine whether the workstation is locked. To receive notification when the user logs in, use the WTSRegisterSessionNotification function to receive WM_WTSSESSION_CHANGE messages. You can use session notifications to track the desktop state so you know whether it is possible to interact with the user.

Damnatory answered 29/3, 2015 at 7:31 Comment(0)
F
5

Alex Vershynin's version works well, with a few code changes that I had to make.

I had to: change DW_WTSSessionInfoEx to WTSSessionInfoEx (Answering user586399), define "g_dwSessionID" to WTSGetActiveConsoleSessionId(), include Wtsapi32.h. I think that's it ...

Since I can't comment, I'll paste the whole code here.

#include "Wtsapi32.h"
bool IsSessionLocked()
{
    typedef BOOL( PASCAL * WTSQuerySessionInformation )( HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned );
    typedef void ( PASCAL * WTSFreeMemory )( PVOID pMemory );

    WTSINFOEXW * pInfo = NULL;
    WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
    bool bRet = false;
    LPTSTR ppBuffer = NULL;
    DWORD dwBytesReturned = 0;
    LONG dwFlags = 0;
    WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
    WTSFreeMemory pWTSFreeMemory = NULL;

    HMODULE hLib = LoadLibrary( "wtsapi32.dll" );
    if( !hLib )
    {
        return false;
    }
    pWTSQuerySessionInformation = (WTSQuerySessionInformation) GetProcAddress( hLib, "WTSQuerySessionInformationW" );
    if( pWTSQuerySessionInformation )
    {
        pWTSFreeMemory = (WTSFreeMemory) GetProcAddress( hLib, "WTSFreeMemory" );
        if( pWTSFreeMemory != NULL )
        {
            DWORD dwSessionID = WTSGetActiveConsoleSessionId();
            if( pWTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned ) )
            {
                if( dwBytesReturned > 0 )
                {
                    pInfo = (WTSINFOEXW*) ppBuffer;
                    if( pInfo->Level == 1 )
                    {
                        dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
                    }
                    if( dwFlags == WTS_SESSIONSTATE_LOCK )
                    {
                        bRet = true;
                    }
                }
                pWTSFreeMemory( ppBuffer );
                ppBuffer = NULL;
            }
        }
    }
    if( hLib != NULL )
    {
        FreeLibrary( hLib );
    }
    return bRet;
}
Faugh answered 27/3, 2017 at 19:56 Comment(0)
K
5

Just as a refinement to MGamsby's post: if you are targeting the Win8.1 SDK or Win10 SDK, then it's not necessary to mess around with GetProcAddress(), LoadLibrary(), and WTSGetActiveConsoleSessionId(), and the code becomes much more compact:

bool isSessionLocked()
{
    WTSINFOEXW* pInfo = NULL;
    WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
    LPTSTR ppBuffer = NULL;
    DWORD dwBytesReturned = 0;
    LONG sessionFlags = WTS_SESSIONSTATE_UNKNOWN; // until we know otherwise. Prevents a false positive since WTS_SESSIONSTATE_LOCK == 0

    DWORD dwSessionID = WTSGetActiveConsoleSessionId();

    if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned))
    {
        if (dwBytesReturned > 0)
        {
            pInfo = (WTSINFOEXW*)ppBuffer;
            if (pInfo->Level == 1)
            {
                sessionFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
            }
        }
        WTSFreeMemory(ppBuffer);
        ppBuffer = NULL;
    }

    return (sessionFlags == WTS_SESSIONSTATE_LOCK);
}

NOTE: I realise that the question is about Windows 7 but you can still target Win7 using the Win8.1 SDK, and it is available for use in VS2015 onwards.

Edit: Oops. I just realised the OP asked for WinXP onwards in which case, yes, you do need to mess around with LoadLibrary() etc. Sorry about that. I'll leave my code here in case it is useful for anyone.

Kor answered 19/1, 2020 at 14:0 Comment(2)
This worked for me after some Changes: - The SessionFlags is a bitset, so I need to interpret the value with sessionFlags & WTS_SESSIONSTATE_LOCK. In the example above it works not reliable. - On Windows 7 the values are reversed due to a bug. See learn.microsoft.com/en-us/windows/win32/api/wtsapi32/…Unilateral
I do not think that is correct to interpret as a bitmask, Malte. The documentation says it can be "one or more" of WTS_SESSIONSTATE_UNKNOWN (4294967295 (0xFFFFFFFF)), WTS_SESSIONSTATE_LOCK (0 (0x0)), or WTS_SESSIONSTATE_UNLOCK (1 (0x1)) If you interpret as a bitmask then your code will interpret WTS_SESSIONSTATE_UNKNOWN as both WTS_SESSIONSTATE_UNLOCK and WTS_SESSIONSTATE_LOCK and I do not think this is what you intend!Kor
D
0

Apparently there is a difference between screen lock and session lock. Here're my investigations results.

A session becomes locked when a password box is shown on the lock screen. In Windows 7 after pressing Win+L lockscreen appears with password box, so a session is locked immediately. But in Win10/11 lockscreen initially appears without password box. And nothing happens. After pressing a key or clicking a mouse button, password box appears and session becomes locked.

  1. WTSRegisterSessionNotification(): WM_WTSSESSION_CHANGE message arrives after password box is shown.

  2. WTSQuerySessionInformation(): sessionFlags == WTS_SESSIONSTATE_LOCK after password box is shown

  3. SetWinEventHook() with EVENT_SYSTEM_DESKTOPSWITCH: multiple events arrives when (un)locking. Looks like two desktops are created when locking: for initial lock screen and the one with password box.

  4. SwitchDesktop() succeeds until password box is shown.

  5. Checking for running LogonUI.exe works, but it remains running for a while after unlock. Theoretically in the future versions it may remain running permanently. So one can't be absolutely sure the session is locked if LogonUI.exe is running.

So far I haven't found a reliable way to check if computer is locked and unlocked. But I'll try to continue my research.

Digamy answered 8/12, 2022 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.