如果用户不在场,他就看不到通知。因此,我想要做的是能够检查用户是否锁定了屏幕或者屏幕保护程序是否已激活。
任何在用户无法看到的情况下触发的通知都将被延迟,并在用户登录并恢复会话时显示。
我自己使用的是Windows 7,但我更喜欢适用于Windows XP及以上版本的解决方案。
目前没有记录的方法可以查找工作站是否已被锁定。但是,您可以在其解锁/锁定时获取通知。订阅SystemEvents.SessionSwitch事件,您将收到SessionSwitchReason.SessionLock和Unlock。
屏幕保护程序也很麻烦。当屏幕保护程序打开时,您的主窗口会收到WM_SYSCOMMAND消息,SC_SCREENSAVE。您可以调用SystemParametersInfo来检查它是否正在运行。在此线程中,您将找到示例代码。
目前没有好的方法可以确定用户是否已经睡着。
我最近从一篇先前的博客文章中 (链接) 再次检查了这段代码,以确保它适用于 Windows XP 到 7 版本,包括 x86 和 x64,并对其进行了简化。
以下是最新的、检查工作站是否锁定并且屏幕保护程序是否正在运行的简洁代码,封装在两个易于使用的静态方法中:
using System;
using System.Runtime.InteropServices;
namespace BrutalDev.Helpers
{
public static class NativeMethods
{
// Used to check if the screen saver is running
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(uint uAction,
uint uParam,
ref bool lpvParam,
int fWinIni);
// Used to check if the workstation is locked
[DllImport("user32", SetLastError = true)]
private static extern IntPtr OpenDesktop(string lpszDesktop,
uint dwFlags,
bool fInherit,
uint dwDesiredAccess);
[DllImport("user32", SetLastError = true)]
private static extern IntPtr OpenInputDesktop(uint dwFlags,
bool fInherit,
uint dwDesiredAccess);
[DllImport("user32", SetLastError = true)]
private static extern IntPtr CloseDesktop(IntPtr hDesktop);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SwitchDesktop(IntPtr hDesktop);
// Check if the workstation has been locked.
public static bool IsWorkstationLocked()
{
const int DESKTOP_SWITCHDESKTOP = 256;
IntPtr hwnd = OpenInputDesktop(0, false, DESKTOP_SWITCHDESKTOP);
if (hwnd == IntPtr.Zero)
{
// Could not get the input desktop, might be locked already?
hwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);
}
// Can we switch the desktop?
if (hwnd != IntPtr.Zero)
{
if (SwitchDesktop(hwnd))
{
// Workstation is NOT LOCKED.
CloseDesktop(hwnd);
}
else
{
CloseDesktop(hwnd);
// Workstation is LOCKED.
return true;
}
}
return false;
}
// Check if the screensaver is busy running.
public static bool IsScreensaverRunning()
{
const int SPI_GETSCREENSAVERRUNNING = 114;
bool isRunning = false;
if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref isRunning, 0))
{
// Could not detect screen saver status...
return false;
}
if (isRunning)
{
// Screen saver is ON.
return true;
}
// Screen saver is OFF.
return false;
}
}
}
更新:根据评论中的建议更新了代码。
当工作站被锁定时,OpenInputDesktop方法不会返回句柄,因此我们可以回退到OpenDesktop以获取一个句柄,并尝试切换来确保它被锁定。如果没有被锁定,则由于OpenInputDesktop将返回您正在查看的桌面的有效句柄,因此不会激活默认桌面。
SystemEvents.SessionSwitch += new SessionSwitchEventHandler((sender, e) =>
{
switch (e.Reason)
{
//If Reason is Lock, Turn off the monitor.
case SessionSwitchReason.SessionLock:
//SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF);
MessageBox.Show("locked");
break;
case SessionSwitchReason.SessionUnlock:
MessageBox.Show("unlocked");
break;
}
});
这将指示会话何时被锁定和解锁。
在研究这个问题时,我发现了几种技术可以支持检测工作站是否已锁定。其中一种非常简单:
bool locked = Process.GetProcessesByName("logonui").Any();
https://superuser.com/questions/1170918/determine-remote-windows-screen-locked-or-unlocked-remotely
http://mctexpert.blogspot.com/2012/10/how-to-determine-if-client-on-your.html
后者需要设置审计策略,这通常是默认的。我在企业IT组织中工作,所以这对我来说不是一个问题,因为我确定这些设置已经应用了。有很多原因会导致用户无法看到您的通知,例如全屏视频播放或用户不在场。
我建议您不要仅检查是否可以显示通知,而是检查用户是否在场,您可以通过监视键盘和鼠标来实现。