检测屏幕保护程序是否处于活动状态和/或用户是否已在Windows中锁定屏幕

Pie*_*ter 7 c# windows

我正在编写一个应用程序,有时会以烤面包机消息的形式向用户发送通知.

如果用户不在,他看不到通知.所以我想做的是能够检查用户是否已锁定屏幕或是否正在激活屏幕保护程序.

当用户重新登录并恢复其会话时,在用户看不到它时触发的任何通知都将被延迟并显示.

我自己在Windows 7上,但我更喜欢一种适用于Windows XP及以上版本的解决方案.

Han*_*ant 26

没有记录的方法来确定工作站当前是否已锁定.但是,当它取消/锁定时,您可以收到通知.订阅SystemEvents.SessionSwitch事件,您将获得SessionSwitchReason.SessionLock和Unlock.

节省时间也很麻烦.当屏幕保护程序打开时,您的主窗口将获取WM_SYSCOMMAND消息SC_SCREENSAVE.您可以pingvoke SystemParametersInfo以检查它是否正在运行.你可以在这个帖子的答案中找到这个示例代码.

没有好的方法可以确定用户是否睡着了.

  • +1"没有好办法知道用户是否睡着了." :) (5认同)

Bru*_*Dev 9

我最近从之前的博客文章中再次检查了此代码,以确保它适用于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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

更新:根据评论中的建议更新代码.

当工作站被锁定时,OpenInputDesktop方法不会返回句柄,因此我们可以在OpenDesktop上回退一个句柄,以确保通过尝试切换来锁定它.如果未锁定,则不会激活默认桌面,因为OpenInputDesktop将返回您正在查看的桌面的有效句柄.

  • 除非我遗漏了一些东西,否则如果在我使用的app中运行计时器,这段代码会让我发疯.非常确定,在未锁定时,将始终强制默认桌面处于活动状态.我使用了多个桌面,并在所有桌面上进行实际工作.我不使用它,但MS有一个desktops.exe应用程序,它允许您创建多个桌面并在它们之间切换,不确定是否有很多人使用它,如果是这样,这段代码不适合生产.我认为有一种方法可以获得当前的输入桌面,这可能是一个更好的检查. (2认同)