<p>How can one determine, in code, how long the machine is locked?</p>

<p>Other ideas outside of C# are also welcome.</p>

<hr>

<p>I like the windows service idea (and have accepted it) for simplicity and cleanliness, but unfortunately I don't think it will work for me in this particular case. I wanted to run this on my workstation at work rather than home (or in addition to home, I suppose), but it's locked down pretty hard courtesy of the DoD. That's part of the reason I'm rolling my own, actually.</p>

<p>I'll write it up anyway and see if it works. Thanks everyone!</p>

[{'answer_id': 44987, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': -1, 'selected': False, 'text': '<p>Below is the 100% working code to find if the PC is locked or not.</p>\n\n<p>Before using this use the namespace <code>System.Runtime.InteropServices</code>.</p>\n\n<pre><code>[DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi,SetLastError = true, ExactSpelling = true)]\nprivate static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess);\n\n[DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]\nprivate static extern Int32 CloseDesktop(Int32 hDesktop);\n\n[DllImport("user32", CharSet = CharSet.Ansi,SetLastError = true,ExactSpelling = true)]\nprivate static extern Int32 SwitchDesktop(Int32 hDesktop);\n\npublic static bool IsWorkstationLocked()\n{\n    const int DESKTOP_SWITCHDESKTOP = 256;\n    int hwnd = -1;\n    int rtn = -1;\n\n    hwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);\n\n    if (hwnd != 0)\n    {\n        rtn = SwitchDesktop(hwnd);\n        if (rtn == 0)\n        {\n            // Locked\n            CloseDesktop(hwnd);\n            return true;\n        }\n        else\n        {\n            // Not locked\n            CloseDesktop(hwnd);\n        }\n    }\n    else\n    {\n        // Error: "Could not access the desktop..."\n    }\n\n    return false;\n}\n</code></pre>\n'}, {'answer_id': 45134, 'author': 'Timothy Carter', 'author_id': 4660, 'author_profile': 'https://Stackoverflow.com/users/4660', 'pm_score': 5, 'selected': False, 'text': '<p>I would create a Windows Service (a visual studio 2005 project type) that handles the OnSessionChange event as shown below:</p>\n\n<pre><code>protected override void OnSessionChange(SessionChangeDescription changeDescription)\n{\n    if (changeDescription.Reason == SessionChangeReason.SessionLock)\n    { \n        //I left my desk\n    }\n    else if (changeDescription.Reason == SessionChangeReason.SessionUnlock)\n    { \n        //I returned to my desk\n    }\n}\n</code></pre>\n\n<p>What and how you log the activity at that point is up to you, but a Windows Service provides quick and easy access to windows events like startup, shutdown, login/out, along with the lock and unlock events.</p>\n'}, {'answer_id': 52102, 'author': 'adeel825', 'author_id': 324, 'author_profile': 'https://Stackoverflow.com/users/324', 'pm_score': 4, 'selected': False, 'text': '<p>The solution below uses the Win32 API.  OnSessionLock is called when the workstation is locked, and OnSessionUnlock is called when it is unlocked.</p>\n\n<pre><code>[DllImport("wtsapi32.dll")]\nprivate static extern bool WTSRegisterSessionNotification(IntPtr hWnd,\nint dwFlags);\n\n[DllImport("wtsapi32.dll")]\nprivate static extern bool WTSUnRegisterSessionNotification(IntPtr\nhWnd);\n\nprivate const int NotifyForThisSession = 0; // This session only\n\nprivate const int SessionChangeMessage = 0x02B1;\nprivate const int SessionLockParam = 0x7;\nprivate const int SessionUnlockParam = 0x8;\n\nprotected override void WndProc(ref Message m)\n{\n    // check for session change notifications\n    if (m.Msg == SessionChangeMessage)\n    {\n        if (m.WParam.ToInt32() == SessionLockParam)\n            OnSessionLock(); // Do something when locked\n        else if (m.WParam.ToInt32() == SessionUnlockParam)\n            OnSessionUnlock(); // Do something when unlocked\n    }\n\n    base.WndProc(ref m);\n    return;\n}\n\nvoid OnSessionLock() \n{\n    Debug.WriteLine("Locked...");\n}\n\nvoid OnSessionUnlock() \n{\n    Debug.WriteLine("Unlocked...");\n}\n\nprivate void Form1Load(object sender, EventArgs e)\n{\n    WTSRegisterSessionNotification(this.Handle, NotifyForThisSession);\n}\n\n// and then when we are done, we should unregister for the notification\n//  WTSUnRegisterSessionNotification(this.Handle);\n</code></pre>\n'}, {'answer_id': 604042, 'author': 'Timothy Carter', 'author_id': 4660, 'author_profile': 'https://Stackoverflow.com/users/4660', 'pm_score': 8, 'selected': True, 'text': "<p>I hadn't found this before, but from any application you can hookup a SessionSwitchEventHandler. Obviously your application will need to be running, but so long as it is:</p>\n\n<pre><code>Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);\n\nvoid SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e)\n{\n    if (e.Reason == SessionSwitchReason.SessionLock)\n    { \n        //I left my desk\n    }\n    else if (e.Reason == SessionSwitchReason.SessionUnlock)\n    { \n        //I returned to my desk\n    }\n}\n</code></pre>\n"}, {'answer_id': 36596656, 'author': 'Robert', 'author_id': 199111, 'author_profile': 'https://Stackoverflow.com/users/199111', 'pm_score': 3, 'selected': False, 'text': '<p>I know this is an old question but i have found a method to get the Lock State for a given session.</p>\n\n<p>I found my answer <a href="https://stackoverflow.com/a/32115698/199111">here</a> but it was in C++ so i translated as much as i can to C# to get the Lock State.</p>\n\n<p>So here goes:</p>\n\n<pre><code>static class SessionInfo {\n    private const Int32 FALSE = 0;\n\n    private static readonly IntPtr WTS_CURRENT_SERVER = IntPtr.Zero;\n\n    private const Int32 WTS_SESSIONSTATE_LOCK = 0;\n    private const Int32 WTS_SESSIONSTATE_UNLOCK = 1;\n\n    private static bool _is_win7 = false;\n\n    static SessionInfo() {\n        var os_version = Environment.OSVersion;\n        _is_win7 = (os_version.Platform == PlatformID.Win32NT && os_version.Version.Major == 6 && os_version.Version.Minor == 1);\n    }\n\n    [DllImport("wtsapi32.dll")]\n    private static extern Int32 WTSQuerySessionInformation(\n        IntPtr hServer,\n        [MarshalAs(UnmanagedType.U4)] UInt32 SessionId,\n        [MarshalAs(UnmanagedType.U4)] WTS_INFO_CLASS WTSInfoClass,\n        out IntPtr ppBuffer,\n        [MarshalAs(UnmanagedType.U4)] out UInt32 pBytesReturned\n    );\n\n    [DllImport("wtsapi32.dll")]\n    private static extern void WTSFreeMemoryEx(\n        WTS_TYPE_CLASS WTSTypeClass,\n        IntPtr pMemory,\n        UInt32 NumberOfEntries\n    );\n\n    private enum WTS_INFO_CLASS {\n        WTSInitialProgram = 0,\n        WTSApplicationName = 1,\n        WTSWorkingDirectory = 2,\n        WTSOEMId = 3,\n        WTSSessionId = 4,\n        WTSUserName = 5,\n        WTSWinStationName = 6,\n        WTSDomainName = 7,\n        WTSConnectState = 8,\n        WTSClientBuildNumber = 9,\n        WTSClientName = 10,\n        WTSClientDirectory = 11,\n        WTSClientProductId = 12,\n        WTSClientHardwareId = 13,\n        WTSClientAddress = 14,\n        WTSClientDisplay = 15,\n        WTSClientProtocolType = 16,\n        WTSIdleTime = 17,\n        WTSLogonTime = 18,\n        WTSIncomingBytes = 19,\n        WTSOutgoingBytes = 20,\n        WTSIncomingFrames = 21,\n        WTSOutgoingFrames = 22,\n        WTSClientInfo = 23,\n        WTSSessionInfo = 24,\n        WTSSessionInfoEx = 25,\n        WTSConfigInfo = 26,\n        WTSValidationInfo = 27,\n        WTSSessionAddressV4 = 28,\n        WTSIsRemoteSession = 29\n    }\n\n    private enum WTS_TYPE_CLASS {\n        WTSTypeProcessInfoLevel0,\n        WTSTypeProcessInfoLevel1,\n        WTSTypeSessionInfoLevel1\n    }\n\n    public enum WTS_CONNECTSTATE_CLASS {\n        WTSActive,\n        WTSConnected,\n        WTSConnectQuery,\n        WTSShadow,\n        WTSDisconnected,\n        WTSIdle,\n        WTSListen,\n        WTSReset,\n        WTSDown,\n        WTSInit\n    }\n\n    public enum LockState {\n        Unknown,\n        Locked,\n        Unlocked\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    private struct WTSINFOEX {\n        public UInt32 Level;\n        public UInt32 Reserved; /* I have observed the Data field is pushed down by 4 bytes so i have added this field as padding. */\n        public WTSINFOEX_LEVEL Data;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    private struct WTSINFOEX_LEVEL {\n        public WTSINFOEX_LEVEL1 WTSInfoExLevel1;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    private struct WTSINFOEX_LEVEL1 {\n        public UInt32 SessionId;\n        public WTS_CONNECTSTATE_CLASS SessionState;\n        public Int32 SessionFlags;\n\n        /* I can\'t figure out what the rest of the struct should look like but as i don\'t need anything past the SessionFlags i\'m not going to. */\n\n    }\n\n    public static LockState GetSessionLockState(UInt32 session_id) {\n        IntPtr ppBuffer;\n        UInt32 pBytesReturned;\n\n        Int32 result = WTSQuerySessionInformation(\n            WTS_CURRENT_SERVER,\n            session_id,\n            WTS_INFO_CLASS.WTSSessionInfoEx,\n            out ppBuffer,\n            out pBytesReturned\n        );\n\n        if (result == FALSE)\n            return LockState.Unknown;\n\n        var session_info_ex = Marshal.PtrToStructure&lt;WTSINFOEX&gt;(ppBuffer);\n\n        if (session_info_ex.Level != 1)\n            return LockState.Unknown;\n\n        var lock_state = session_info_ex.Data.WTSInfoExLevel1.SessionFlags;\n        WTSFreeMemoryEx(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, ppBuffer, pBytesReturned);\n\n        if (_is_win7) {\n            /* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019(v=vs.85).aspx\n                * Windows Server 2008 R2 and Windows 7:  Due to a code defect, the usage of the WTS_SESSIONSTATE_LOCK\n                * and WTS_SESSIONSTATE_UNLOCK flags is reversed. That is, WTS_SESSIONSTATE_LOCK indicates that the\n                * session is unlocked, and WTS_SESSIONSTATE_UNLOCK indicates the session is locked.\n                * */\n            switch (lock_state) {\n                case WTS_SESSIONSTATE_LOCK:\n                    return LockState.Unlocked;\n\n                case WTS_SESSIONSTATE_UNLOCK:\n                    return LockState.Locked;\n\n                default:\n                    return LockState.Unknown;\n            }\n        }\n        else {\n            switch (lock_state) {\n                case WTS_SESSIONSTATE_LOCK:\n                    return LockState.Locked;\n\n                case WTS_SESSIONSTATE_UNLOCK:\n                    return LockState.Unlocked;\n\n                default:\n                    return LockState.Unknown;\n            }\n        }\n    }\n}</code></pre>\n\n<p>Note: The above code was extracted from a much larger project so if i missed a bit sorry.  I havn\'t got time to test the above code but plan to come back in a week or two to check everything.  I only posted it now because i didn\'t want to forget to do it.</p>\n'}, {'answer_id': 42634766, 'author': 'Abdul Rahman Kayali', 'author_id': 236384, 'author_profile': 'https://Stackoverflow.com/users/236384', 'pm_score': 3, 'selected': False, 'text': '<p><strong>NOTE</strong>: This is not an answer, but a (contribution) to <a href="https://stackoverflow.com/users/4660/timothy-carter">Timothy Carter</a> answer, because my reputation doesn\'t allow me to comment so far.</p>\n\n<p>Just in case somebody tried the code from Timothy Carter\'s answer and did not get it to work right away in a Windows service, there\'s one property that need to be set to <code>true</code> in the constructor of the service.\nJust add the line in the constructor:</p>\n\n<pre><code>CanHandleSessionChangeEvent = true;\n</code></pre>\n\n<p>And be sure not to set this property after the service is started otherwise an <code>InvalidOperationException</code> will be thrown.</p>\n'}, {'answer_id': 47080750, 'author': 'granadaCoder', 'author_id': 214977, 'author_profile': 'https://Stackoverflow.com/users/214977', 'pm_score': 2, 'selected': False, 'text': '<p>If you\'re interested in writing a windows-service to "find" these events, topshelf (the library/framework that makes writing windows services much easier) has a hook.</p>\n\n<pre><code>public interface IMyServiceContract\n{\n    void Start();\n\n    void Stop();\n\n    void SessionChanged(Topshelf.SessionChangedArguments args);\n}\n\n\n\npublic class MyService : IMyServiceContract\n{\n\n    public void Start()\n    {\n    }\n\n    public void Stop()\n    {\n\n    }\n\n    public void SessionChanged(SessionChangedArguments e)\n    {\n        Console.WriteLine(e.ReasonCode);\n    }   \n}\n</code></pre>\n\n<p>and now the code to wire up the topshelf service to the interface/concrete above</p>\n\n<p>Everything below is "typical" topshelf setup.... except for 2 lines which I marked as</p>\n\n<p>/* THIS IS MAGIC LINE */</p>\n\n<p>Those are what get the SessionChanged method to fire.</p>\n\n<p>I tested this with windows 10 x64.  I locked and unlocked my machine and I got the desired result.</p>\n\n<pre><code>            IMyServiceContract myServiceObject = new MyService(); /* container.Resolve&lt;IMyServiceContract&gt;(); */\n\n\n            HostFactory.Run(x =&gt;\n            {\n                x.Service&lt;IMyServiceContract&gt;(s =&gt;\n                {\n                    s.ConstructUsing(name =&gt; myServiceObject);\n                    s.WhenStarted(sw =&gt; sw.Start());\n                    s.WhenStopped(sw =&gt; sw.Stop());\n                    s.WhenSessionChanged((csm, hc, chg) =&gt; csm.SessionChanged(chg)); /* THIS IS MAGIC LINE */\n                });\n\n                x.EnableSessionChanged(); /* THIS IS MAGIC LINE */\n\n                /* use command line variables for the below commented out properties */\n                /*\n                x.RunAsLocalService();\n                x.SetDescription("My Description");\n                x.SetDisplayName("My Display Name");\n                x.SetServiceName("My Service Name");\n                x.SetInstanceName("My Instance");\n                */\n\n                x.StartManually(); // Start the service manually.  This allows the identity to be tweaked before the service actually starts\n\n                /* the below map to the "Recover" tab on the properties of the Windows Service in Control Panel */\n                x.EnableServiceRecovery(r =&gt;\n                {\n                    r.OnCrashOnly();\n                    r.RestartService(1); ////first\n                    r.RestartService(1); ////second\n                    r.RestartService(1); ////subsequents\n                    r.SetResetPeriod(0);\n                });\n\n                x.DependsOnEventLog(); // Windows Event Log\n                x.UseLog4Net();\n\n                x.EnableShutdown();\n\n                x.OnException(ex =&gt;\n                {\n                    /* Log the exception */\n                    /* not seen, I have a log4net logger here */\n                });\n            });                 \n</code></pre>\n\n<p>My packages.config to provide hints about versions:</p>\n\n<pre><code>  &lt;package id="log4net" version="2.0.5" targetFramework="net45" /&gt;\n  &lt;package id="Topshelf" version="4.0.3" targetFramework="net461" /&gt;\n  &lt;package id="Topshelf.Log4Net" version="4.0.3" targetFramework="net461" /&gt;\n</code></pre>\n'}, {'answer_id': 63944711, 'author': 'Nathan', 'author_id': 294317, 'author_profile': 'https://Stackoverflow.com/users/294317', 'pm_score': 0, 'selected': False, 'text': "<p>In Windows Task Scheduler, you could create tasks that trigger <code>on workstation lock</code> and <code>on workstation unlock</code>.  Each task could write a flag and timestamp to a file to state if the workstation is locked or unlocked and when it happened.</p>\n<p>I realize that this is not a programmatic way.  It is simpler than writing a service.  It won't miss an event because your program happens to not be running at the time of lock/unlock transition.</p>\n"}]
['https://Stackoverflow.com/questions/44980', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/1588/']