3

How do I detect the current state of the Windows (unlocked/locked) in C#? All of the examples I've found deal with an event handler which is triggered by the unlocking or locking of windows. I am not interested in an event handler. (Sorry for the bold.) I just want to know the current state (unlocked or locked) of windows. The closest I've found is a mention to System.Security.Principal.WindowsIdentity.GetCurrent(), but I don't know how to use this.

A workaround for my specific code would be if there is a function that I could call that would say IsGUIViewable. However, I would still like an answer to the original question if possible.

Sean
  • 336
  • 1
  • 3
  • 15
  • 2
    You just can't find out, stop looking. – Hans Passant Mar 11 '16 at 14:37
  • That deals with an event handler that triggers when Windows is locked or unlocked. I am not interested in something firing when Windows is locked or unlocked. I just want the current state. Please read my question. – Sean Mar 11 '16 at 14:39
  • this post says you can use OpenInputDesktop, sounds like a bit of a hack to me though... https://code.msdn.microsoft.com/windowsapps/CSDetectWindowsSessionState-f27d0321 – Jay Mar 11 '16 at 14:39
  • http://stackoverflow.com/questions/44980/how-can-i-programmatically-determine-if-my-workstation-is-locked – Tamas Ionut Mar 11 '16 at 14:40
  • This SO question has some good answers which might help you. [Detect if desktop is locked](http://stackoverflow.com/questions/768314/detect-if-desktop-is-locked) – Jason Evans Mar 11 '16 at 14:43
  • 1
    Why do you think there *is* a state you can check? Why do you even think you are running in a user GUI session in the first place? What about the other sessions on the same machine, does it bother you if they are locked? Why do you care in the first place? If you know about the event handlers, why not maintain the state yourself? – Luaan Mar 11 '16 at 14:47
  • @JasonEvans Thank you, but the only answer to that question is coded in C++ with functions whose equivalent in C# I don't know. – Sean Mar 11 '16 at 14:48
  • @Luaan I care because I need to know the state, unlocked or locked, of the machine so that one branch of the process doesn't run while the machine is locked. The process can be started while the machine is locked. I don't know if there is a state I can check. That's why I am asking the question. – Sean Mar 11 '16 at 14:51
  • You can call any native function using P/Invoke, so C++ examples are still relevant. However, all of them are hacks, and bound to fail at some point in the future (and make Raymond cry). What part shouldn't run in a locked system and why? For example, GUI will automatically stop refreshing etc., so some applications simply check if there was a paint message recently (while asking themselves to repaint). Do you also care about whether the session is local or remote? Do you consider a disconnected remote session "locked"? How is your app run in a locked session, Task Scheduler? – Luaan Mar 11 '16 at 14:55
  • @Luaan I have a portion of the process which takes a snapshot of the current application view and emails it. I don't want that process to run. The process is local. – Sean Mar 11 '16 at 15:00
  • As in the snapshot of the GUI? If that's the case, I don't think you should have any trouble - no WM_PAINTs will be sent when the session is locked, so all you need is to tie your snapshotting to the painting process (which it needs anyway), and you're fine. If it's a snapshot of some data, the same basic approach still works - set a flag, do an `Invalidate`, and send the snapshot (ideally asynchronously) in `OnPaint` if the flag is set. In a locked session, the snapshot will never be collected or sent. – Luaan Mar 11 '16 at 15:21
  • @Luaan How do I tie the snapshotting to the painting process? – Sean Mar 11 '16 at 15:28
  • One way is described in the other half of the comment :) But it should really already work on its own, without any input - what are you using to collect that snapshot? I assume it either returns an error, some sort of a blank value or blocks if the session is locked. Getting windows to render on a non-GUI session is rather tricky, kind of like trying to render a minimized window. If all else fails, you could try using this: https://msdn.microsoft.com/en-us/library/aa379437(VS.85).aspx It's going to take some time to get right, and it only works on XP+, though. It's what taskman uses, IIRC. – Luaan Mar 11 '16 at 15:36

1 Answers1

1

I have also faced the same problem. I was used SystemEvents.SessionSwitch in this case but this snippet was useless until user change the session. Here below is the workable code after observing bunch of post regarding this.

Define Your Native Enum class:

"NativeEnums.cs"
public enum WTS_INFO_CLASS
{
    WTSInitialProgram = 0,
    WTSApplicationName = 1,
    WTSWorkingDirectory = 2,
    WTSOEMId = 3,
    WTSSessionId = 4,
    WTSUserName = 5,
    WTSWinStationName = 6,
    WTSDomainName = 7,
    WTSConnectState = 8,
    WTSClientBuildNumber = 9,
    WTSClientName = 10,
    WTSClientDirectory = 11,
    WTSClientProductId = 12,
    WTSClientHardwareId = 13,
    WTSClientAddress = 14,
    WTSClientDisplay = 15,
    WTSClientProtocolType = 16,
    WTSIdleTime = 17,
    WTSLogonTime = 18,
    WTSIncomingBytes = 19,
    WTSOutgoingBytes = 20,
    WTSIncomingFrames = 21,
    WTSOutgoingFrames = 22,
    WTSClientInfo = 23,
    WTSSessionInfo = 24,
    WTSSessionInfoEx = 25,
    WTSConfigInfo = 26,
    WTSValidationInfo = 27,
    WTSSessionAddressV4 = 28,
    WTSIsRemoteSession = 29
}

public enum WTS_TYPE_CLASS
{
    WTSTypeProcessInfoLevel0,
    WTSTypeProcessInfoLevel1,
    WTSTypeSessionInfoLevel1
}

public enum WTS_CONNECTSTATE_CLASS
{
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
}

public enum LockState
{
    Unknown,
    Locked,
    Unlocked
}

Here below is the related Structs:

public class NativeStructs
{
    [StructLayout(LayoutKind.Sequential)]
    public struct WTSINFOEX
    {
        public UInt32 Level;
        public UInt32 Reserved;
        public WTSINFOEX_LEVEL Data;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WTSINFOEX_LEVEL
    {
        public WTSINFOEX_LEVEL1 WTSInfoExLevel1;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WTSINFOEX_LEVEL1
    {
        public UInt32 SessionId;
        public WTS_CONNECTSTATE_CLASS SessionState;
        public Int32 SessionFlags;
    }
}

Define your Native Methods:

public class NativeMethods
{
    [DllImport("wtsapi32.dll")]
    public static extern Int32 WTSQuerySessionInformation(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] UInt32 SessionId, [MarshalAs(UnmanagedType.U4)] WTS_INFO_CLASS WTSInfoClass, out IntPtr ppBuffer, [MarshalAs(UnmanagedType.U4)] out UInt32 pBytesReturned);

    [DllImport("wtsapi32.dll")]
    public static extern void WTSFreeMemoryEx(WTS_TYPE_CLASS WTSTypeClass, IntPtr pMemory, UInt32 NumberOfEntries);

    [DllImport("kernel32.dll")]
    public static extern uint WTSGetActiveConsoleSessionId();
}

Now define your API controller class:

public sealed class SessionManager
{
    private static readonly Lazy<SessionManager> _sessionManager = new Lazy<SessionManager>(() => new SessionManager());
    private const Int32 FALSE = 0;

    private static readonly IntPtr WTS_CURRENT_SERVER = IntPtr.Zero;
    private const Int32 WTS_SESSIONSTATE_LOCK = 0;
    private const Int32 WTS_SESSIONSTATE_UNLOCK = 1;

    private static bool _is_win7 = false;

    public static SessionManager GetInstance()
    {
        return _sessionManager.Value;
    }

    public UInt32 GetSessionId()
    {
        UInt32 result = 0;
        result = NativeMethods.WTSGetActiveConsoleSessionId();
        if (result == 0xFFFFFFFF)
        {
            App.LOG(LogLevel.Standard, "No session attached to the physical console.");
        }   

        return result;
    }

    public LockState GetSessionLockState()
    {
        try
        {
            IntPtr ppBuffer;
            UInt32 pBytesReturned;

            UInt32 session_id = GetSessionId();
            Int32 result = NativeMethods.WTSQuerySessionInformation(WTS_CURRENT_SERVER, session_id, WTS_INFO_CLASS.WTSSessionInfoEx, out ppBuffer, out pBytesReturned);

            if (result == FALSE)
                return LockState.Unknown;

            if (pBytesReturned > 0)
            {
                var session_info_ex = Marshal.PtrToStructure<NativeStructs.WTSINFOEX>(ppBuffer);

                if (session_info_ex.Level != 1)
                    return LockState.Unknown;

                var lock_state = session_info_ex.Data.WTSInfoExLevel1.SessionFlags;
                NativeMethods.WTSFreeMemoryEx(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, ppBuffer, pBytesReturned);

                var os_version = Environment.OSVersion;
                _is_win7 = (os_version.Platform == PlatformID.Win32NT && os_version.Version.Major == 6 && os_version.Version.Minor == 1);
                if (_is_win7)
                {
                    /* Due to a code defect,Session state logic is revers to upper windows version */
                    switch (lock_state)
                    {
                        case WTS_SESSIONSTATE_LOCK:
                            return LockState.Unlocked;

                        case WTS_SESSIONSTATE_UNLOCK:
                            return LockState.Locked;

                        default:
                            return LockState.Unknown;
                    }
                }
                else
                {
                    switch (lock_state)
                    {
                        case WTS_SESSIONSTATE_LOCK:
                            return LockState.Locked;

                        case WTS_SESSIONSTATE_UNLOCK:
                            return LockState.Unlocked;

                        default:
                            return LockState.Unknown;
                    }
                }
            }
            else
            {
                return LockState.Unknown;
            }
        }
        catch(Exception ex)
        {
            App.LOG(LogLevel.Standard, $"[Exception] : {ex.ToString()}");
            return LockState.Unknown;
        }
    }
}

Now calling this method from your needed class:

LockState lockState = SessionManager.GetInstance().GetSessionLockState();

            LOG(LogLevel.Standard, $"PC screen lock: {lockState.ToString()}");

            if (lockState == LockState.Locked)
            {
                //Writedown lock screen logic here....
            }
yeasir007
  • 2,110
  • 2
  • 28
  • 43