Как я могу программно определить, заблокирована ли моя рабочая станция?
Я пишу некоторые инструменты производительности / метрики для себя, чтобы помочь контролировать мой фокус в течение дня. Недавно я заметил, что я, как правило, сбиваюсь с пути больше, чем обычно, и чувствую необходимость вставать и идти на прогулки/напитки/и т. д., и я обеспокоен тем, что я "трачу" слишком много времени.
Так как я всегда блокирую свой компьютер, когда я иду куда угодно, и я открываю его, как только я вернусь (даже если я просто читаю на своем столе и т. д.), Мне было интересно, как я могу определить, в коде, как долго машина заблокирована.
Я пишу это в C#, если это помогает, Но я открыт для других идей.
Мне нравится идея службы windows (и приняли его) для простоты и чистоты, но, к сожалению, я не думаю, что это будет работать для меня в данном конкретном случае. Я хотел запустить это на своей рабочей станции на работе, а не дома (или в дополнение к дому, я полагаю), но он заблокирован довольно жестко любезно со стороны Министерства обороны. Это одна из причин, по которой я катаюсь сам, на самом деле.
Я все равно напишу его и посмотрю, работает ли он. Спасибо всем!
7 ответов:
Я не нашел это раньше, но из любого приложения вы можете подключить 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 } }
Я бы создал службу Windows (тип проекта visual studio 2005), которая обрабатывает событие OnSessionChange, как показано ниже:
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 } }что и как вы регистрируете активность в этот момент зависит от вас, но служба Windows обеспечивает быстрый и легкий доступ к событиям windows, таким как запуск, завершение работы, вход/выход, а также события блокировки и разблокировки.
Решение ниже использует Win32 API. OnSessionLock вызывается, когда рабочая станция заблокирована, и 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);
Я знаю, что это старый вопрос, но я нашел способ получить состояние блокировки для данного сеанса.
Я нашел свой ответ здесь но это было на C++, поэтому я перевел столько, сколько мог, на C#, чтобы получить состояние блокировки.
Так вот:
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; } } } }Примечание: приведенный выше код был извлечен из гораздо более крупного проекта, так что если я пропустил немного жаль. У меня нет времени, чтобы проверить приведенный выше код, но планирую вернуться через неделю или две, чтобы проверить все. Я только разместил его сейчас, потому что я не хотел забывать об этом.
Примечание: Это не ответ, а (вклад) в Тимоти Картер ответ, потому что моя репутация не позволяет мне до сих пор комментируют.
на всякий случай, если кто-то попробовал код из ответа Тимоти Картера и не получил его сразу же в службе Windows, есть одно свойство, которое нужно установить в
trueв конструкторе сервиса. Просто добавьте строку в конструктор:CanHandleSessionChangeEvent = true;и не забудьте установить это свойство после запуска службы в противном случае
InvalidOperationExceptionбудет брошен.
Если вы заинтересованы в написании службы windows, чтобы "найти" эти события, topshelf (библиотека/фреймворк, который делает написание служб 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); } }и теперь код для подключения службы topshelf к интерфейсу / бетону выше
все ниже-это" типичная " настройка верхней полки.... за исключением 2 строк, которые я отметил как
/ * ЭТО МАГИЧЕСКАЯ ЛИНИЯ */
Это то, что получить sessionchanged метод огонь.
Я проверил это с windows 10 x64. Я запер и отпер свою машину, и я получил желаемый результат.
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 */ }); });мои пакеты.конфигурация для предоставления подсказок о версиях:
<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" />
ниже-это 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; }
Comments