9

我正在编写仅在 PC 上没有人为活动时才需要运行的代码,例如在屏幕保护程序运行时。关于如何在 Windows 下的 c++ 中执行此操作的任何建议?

@talnicolas,只是为了使用未使用的资源,有多少次人们离开计算机但他们在另一个地方?

4

6 回答 6

13

您可以使用GetLastInputInfo来检查用户空闲了多长时间(没有在鼠标周围移动或在键盘上键入内容)并SystemParametersInfo检查屏幕保护程序是否处于活动状态。

例子

#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>

// do something after 10 minutes of user inactivity
static const unsigned int idle_milliseconds = 60*10*1000;
// wait at least an hour between two runs
static const unsigned int interval = 60*60*1000;

int main() {
    LASTINPUTINFO last_input;
    BOOL screensaver_active;

    // main loop to check if user has been idle long enough
    for (;;) {
        if ( !GetLastInputInfo(&last_input)
          || !SystemParametersInfo(SPI_GETSCREENSAVERACTIVE, 0,  
                                   &screensaver_active, 0))
        {
            std::cerr << "WinAPI failed!" << std::endl;
            return ERROR_FAILURE;
        }

        if (last_input.dwTime < idle_milliseconds && !screensaver_active) {
            // user hasn't been idle for long enough
            // AND no screensaver is running
            Sleep(1000);
            continue;
        }

        // user has been idle at least 10 minutes
        do_something();
        // done. Wait before doing the next loop.
        Sleep(interval);
    }
}

请注意,我在 Linux 机器上编写了该代码,因此无法对其进行测试。

于 2012-01-11T14:10:53.933 回答
7

这只是Niklas B. answer 的一个小更新,已在 Windows 8.1 和 Windows 10 上进行了测试。

变更说明如下:

  • LASTINPUTINFOcbSize初始化

  • 而不是检查SPI_GETSCREENSAVERACTIVESPI_GETSCREENSAVERRUNNING被检查

  • idle_time计算并与idle_milliseconds

  • 仅在更改时通知用户,每约 500 毫秒执行一次检查

  • 由于 Windows 可以在 4802 之后立即触发事件 id 4803,因此可以确定屏幕保护程序仍在用户中打开idle_time,请参阅我遇到的问题

此外,这仅在屏幕保护程序计时器小于屏幕关机计时器时才有效!

#define start

#include <windows.h>
#include <iostream>

// do something after 10 minutes of user inactivity
static const unsigned int idle_milliseconds = 60*10*1000;

int main() {
    LASTINPUTINFO last_input;
    // without setting cbSize GetLastError() returns the parameter is incorrect
    last_input.cbSize = sizeof(last_input);  
    BOOL screensaver_running;

    DWORD idle_time;
    bool screensaverOn = false;

    // main loop to check if user has been idle long enough
    for (;;) {
        if ( !GetLastInputInfo( &last_input )
          || !SystemParametersInfo( SPI_GETSCREENSAVERRUNNING, 0,  
                               &screensaver_running, 0 ) )
        {
            std::cerr << "WinAPI failed!" << std::endl;
            return ERROR_FAILURE;
        }

        // calculate idle time
        idle_time = GetTickCount() - last_input.dwTime;

        if ( idle_time > this->user_idle_time && TRUE == screensaver_running )
        {
            if ( !screensaverOn )
            {
                // screensaver is running
                screensaverOn = true;
                notify( true );
            }
        }
        else if ( idle_time < this->user_idle_time && screensaverOn )
        {
            // screensaver is not running
            screensaverOn = false;
            notify( false );
        }
        // wait 500ms before next loop
        Sleep( 500 );
    }
}
于 2016-04-29T19:16:37.143 回答
3

使用 SystemParametersInfo(...)

   private const int SPI_GETSCREENSAVERACTIVE = 16;
   SystemParametersInfo( SPI_GETSCREENSAVERACTIVE, 0, 
         ref isActive, 0 );
      return isActive;

参考: http: //www.codeproject.com/KB/cs/ScreenSaverControl.aspx

于 2012-01-11T14:13:01.817 回答
1

也许这里的某些人对 plii.dwTime 感到困惑

plii.dwTime 给出最后输入的时间戳日期,它不给出现在和最后输入之间的时间

为此,您必须将其分给 GetTickCount() :

LASTINPUTINFO plii;
plii.cbSize = sizeof(LASTINPUTINFO);
plii.dwTime = 0;

if (GetLastInputInfo(&plii) != 0)
{
    cout << "Last activity  : " <<  GetTickCount() - plii.dwTime << " (ms)" << endl;
}
else {
    cout << "GetLastInputInfo ERROR" << endl;
}
于 2020-12-18T09:23:52.120 回答
0

检查这个看起来相似的答案。

然而你需要确定究竟是“不活动”的意思。这会修改您问题的范围。

还要检查此代码

hdesk = OpenDesktop(TEXT("Screen-saver"),
                    0,
                    FALSE,
                    DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hdesk)
{
   EnumDesktopWindows (hdesk, (WNDENUMPROC)KillScreenSaverFunc, 0);
   CloseDesktop (hdesk);
}

// ----------------------------------------------------------------

BOOL CALLBACK KillScreenSaverFunc (HWND hwnd, LPARAM lParam)
{
   PostMessage(hwnd, WM_CLOSE, 0, 0);
   return TRUE;
}

这里

于 2012-01-11T14:13:11.890 回答
0

无法检测服务中的用户输入是设计强制执行的

这意味着,除其他事项外,服务无法直接知道用户上次输入发生的时间,这也是GetLastInputInfo从服务中调用时没有返回任何有用信息的原因。

显然,这个想法是为了让那些试图编写键盘记录器和其他令人讨厌的代码的狂热黑客的生活更加困难。当然,您可以通过让用户进程将该信息提供给您的服务来轻松绕过该限制。
我不认为这种微不足道的解决方法是普通的狂热黑客无法企及的,我宁愿想象那些在代码卷积中进行如此毫无意义的练习的人。

如果您的服务执行用户请求的计算,那么您很可能需要一个用户进程与之交互。但是,如果您想轮询任何用户活动指示,则必须让该用户进程保持运行。一个用户恶魔一直活着,只是为了给你真正的恶魔提供微不足道的信息。谈论有效利用资源...

附带说明一下,监视屏幕保护程序的激活是非常不可靠的。屏幕可以设置为在任何保护程序出现之前关闭。PC 也可以在您的代码有机会运行之前进入睡眠状态。因此,您必须更加小心检查的条件。

或者,您可能想查看性能计数器
它们可以为您提供大量有关 CPU 负载、磁盘使用情况等的信息。
您可以通过监控这些相对容易地检测到 CPU 和/或磁盘活动低的时期。

该界面虽然是一团糟,但显然还有其他安全障碍需要跳过。我不太确定哪些服务帐户(如果有)可以访问它们,或者它们是否可以在典型的 Windows 安装中默认访问它们。

您也可以在没有人登录时激活您的代码。显然没有明显的方法可以检测到这一点,但它可以在服务中实现。

于 2020-01-10T08:41:49.360 回答