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