我怎样才能以编程方式确定我的工作站是否被locking?

我正在为自己编写一些生产力/度量工具,以帮助监控我整天的工作。 最近,我注意到我往往比平常更加偏离轨道,觉得有必要起身去散步/饮料等,我担心我浪费了太多的时间。

因为当我到任何地方时,我总是locking我的电脑,而且一旦我回来(即使我只是在我的桌子上阅读等),我就解锁它,我想知道如何在代码中确定机器多久被锁住了。

我正在用C#编写这个,如果有帮助的话,但我可以接受其他想法。


我喜欢简单和清洁的Windows服务理念(并已接受它),但不幸的是,在这种情况下,我不认为它适用于我。 我想在我的工作站上运行这个工作,而不是在家里(或者除了家庭之外,我想),但是它被DoDlocking得很困难。 实际上,这是我自己翻身的原因之一。

无论如何,我会把它写出来,看看它是否有效。 感谢大家!

我以前没有find,但从任何应用程序,你可以连接SessionSwitchEventHandler。 显然你的应用程序将需要运行,但只要是:

Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch); void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e) { if (e.Reason == SessionSwitchReason.SessionLock) { //I left my desk } else if (e.Reason == SessionSwitchReason.SessionUnlock) { //I returned to my desk } } 

我将创build一个处理OnSessionChange事件的Windows服务(Visual Studio 2005项目types),如下所示:

  protected override void OnSessionChange(SessionChangeDescription changeDescription) { if (changeDescription.Reason == SessionChangeReason.SessionLock) { //I left my desk } else if (changeDescription.Reason == SessionChangeReason.SessionUnlock) { //I returned to my desk } } 

什么以及如何logging在这一点上的活动取决于你,但Windows服务提供快速和轻松访问Windows事件,如启动,关机,login/出,以及locking和解锁事件。

下面的解决scheme使用Win32 API。 OnSessionLock在工作站被locking时被调用,OnSessionUnlock在被解锁时被调用。

 [DllImport("wtsapi32.dll")] private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags); [DllImport("wtsapi32.dll")] private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd); private const int NotifyForThisSession = 0; // This session only private const int SessionChangeMessage = 0x02B1; private const int SessionLockParam = 0x7; private const int SessionUnlockParam = 0x8; protected override void WndProc(ref Message m) { // check for session change notifications if (m.Msg == SessionChangeMessage) { if (m.WParam.ToInt32() == SessionLockParam) OnSessionLock(); // Do something when locked else if (m.WParam.ToInt32() == SessionUnlockParam) OnSessionUnlock(); // Do something when unlocked } base.WndProc(ref m); return; } void OnSessionLock() { Debug.WriteLine("Locked..."); } void OnSessionUnlock() { Debug.WriteLine("Unlocked..."); } private void Form1Load(object sender, EventArgs e) { WTSRegisterSessionNotification(this.Handle, NotifyForThisSession); } // and then when we are done, we should unregister for the notification // WTSUnRegisterSessionNotification(this.Handle); 

我知道这是一个老问题,但我已经find了一个方法来获得一个给定的会话locking状态。

我发现我的答案在这里,但它是在C ++中,所以我尽我所能地转换到C#来获得locking状态。

所以在这里:

 static class SessionInfo { 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; static SessionInfo() { var os_version = Environment.OSVersion; _is_win7 = (os_version.Platform == PlatformID.Win32NT && os_version.Version.Major == 6 && os_version.Version.Minor == 1); } [DllImport("wtsapi32.dll")] private 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")] private static extern void WTSFreeMemoryEx( WTS_TYPE_CLASS WTSTypeClass, IntPtr pMemory, UInt32 NumberOfEntries ); private 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 } private 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 } [StructLayout(LayoutKind.Sequential)] private struct WTSINFOEX { public UInt32 Level; public UInt32 Reserved; /* I have observed the Data field is pushed down by 4 bytes so i have added this field as padding. */ public WTSINFOEX_LEVEL Data; } [StructLayout(LayoutKind.Sequential)] private struct WTSINFOEX_LEVEL { public WTSINFOEX_LEVEL1 WTSInfoExLevel1; } [StructLayout(LayoutKind.Sequential)] private struct WTSINFOEX_LEVEL1 { public UInt32 SessionId; public WTS_CONNECTSTATE_CLASS SessionState; public Int32 SessionFlags; /* 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. */ } public static LockState GetSessionLockState(UInt32 session_id) { IntPtr ppBuffer; UInt32 pBytesReturned; Int32 result = WTSQuerySessionInformation( WTS_CURRENT_SERVER, session_id, WTS_INFO_CLASS.WTSSessionInfoEx, out ppBuffer, out pBytesReturned ); if (result == FALSE) return LockState.Unknown; var session_info_ex = Marshal.PtrToStructure<WTSINFOEX>(ppBuffer); if (session_info_ex.Level != 1) return LockState.Unknown; var lock_state = session_info_ex.Data.WTSInfoExLevel1.SessionFlags; WTSFreeMemoryEx(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, ppBuffer, pBytesReturned); if (_is_win7) { /* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019(v=vs.85).aspx * Windows Server 2008 R2 and Windows 7: Due to a code defect, the usage of the WTS_SESSIONSTATE_LOCK * and WTS_SESSIONSTATE_UNLOCK flags is reversed. That is, WTS_SESSIONSTATE_LOCK indicates that the * session is unlocked, and WTS_SESSIONSTATE_UNLOCK indicates the session is locked. * */ 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; } } } } 

注意:上面的代码是从一个更大的项目中提取的,所以如果我错过了一点抱歉。 我没有时间去testing上面的代码,但打算在一两个星期后回来查看一切。 我现在只发布了,因为我不想忘记去做。

注意 :这不是一个答案,而是(提供) 提摩太·卡特的答案,因为我的名声不允许我评论到目前为止。

为了防止有人从Timothy Carter的回答中尝试了代码,并且没有立即在Windows服务中使用它,需要在服务的构造函数中将一个属性设置为true 。 只需在构造函数中添加行即可:

 CanHandleSessionChangeEvent = true; 

在服务启动之后请不要设置这个属性,否则会抛出InvalidOperationExceptionexception。

如果你有兴趣编写一个windows服务来“查找”这些事件,那么顶层(使得编写windows服务的库/框架更容易)就有了一个钩子。

 public interface IMyServiceContract { void Start(); void Stop(); void SessionChanged(Topshelf.SessionChangedArguments args); } public class MyService : IMyServiceContract { public void Start() { } public void Stop() { } public void SessionChanged(SessionChangedArguments e) { Console.WriteLine(e.ReasonCode); } } 

现在是将顶层服务连接到上面的接口/具体的代码

下面的一切都是“典型的”顶级设置….除了我标记为2行

/ *这是神奇的线* /

这些是什么让SessionChanged方法触发。

我用Windows 10 x64testing了这个。 我locking和解锁我的机器,我得到了预期的结果。

  IMyServiceContract myServiceObject = new MyService(); /* container.Resolve<IMyServiceContract>(); */ HostFactory.Run(x => { x.Service<IMyServiceContract>(s => { s.ConstructUsing(name => myServiceObject); s.WhenStarted(sw => sw.Start()); s.WhenStopped(sw => sw.Stop()); s.WhenSessionChanged((csm, hc, chg) => csm.SessionChanged(chg)); /* THIS IS MAGIC LINE */ }); x.EnableSessionChanged(); /* THIS IS MAGIC LINE */ /* use command line variables for the below commented out properties */ /* x.RunAsLocalService(); x.SetDescription("My Description"); x.SetDisplayName("My Display Name"); x.SetServiceName("My Service Name"); x.SetInstanceName("My Instance"); */ x.StartManually(); // Start the service manually. This allows the identity to be tweaked before the service actually starts /* the below map to the "Recover" tab on the properties of the Windows Service in Control Panel */ x.EnableServiceRecovery(r => { r.OnCrashOnly(); r.RestartService(1); ////first r.RestartService(1); ////second r.RestartService(1); ////subsequents r.SetResetPeriod(0); }); x.DependsOnEventLog(); // Windows Event Log x.UseLog4Net(); x.EnableShutdown(); x.OnException(ex => { /* Log the exception */ /* not seen, I have a log4net logger here */ }); }); 

我的packages.config提供有关版本的提示:

  <package id="log4net" version="2.0.5" targetFramework="net45" /> <package id="Topshelf" version="4.0.3" targetFramework="net461" /> <package id="Topshelf.Log4Net" version="4.0.3" targetFramework="net461" /> 

下面是findPC是否被locking的100%工作代码。

在使用这个之前使用命名空间System.Runtime.InteropServices

 [DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi,SetLastError = true, ExactSpelling = true)] private static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess); [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] private static extern Int32 CloseDesktop(Int32 hDesktop); [DllImport("user32", CharSet = CharSet.Ansi,SetLastError = true,ExactSpelling = true)] private static extern Int32 SwitchDesktop(Int32 hDesktop); public static bool IsWorkstationLocked() { const int DESKTOP_SWITCHDESKTOP = 256; int hwnd = -1; int rtn = -1; hwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP); if (hwnd != 0) { rtn = SwitchDesktop(hwnd); if (rtn == 0) { // Locked CloseDesktop(hwnd); return true; } else { // Not locked CloseDesktop(hwnd); } } else { // Error: "Could not access the desktop..." } return false; }