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

これは、Windows 8.1 および Windows 10 でテストされたNiklas B. answerのマイナー アップデートです。

変更の説明は次のとおりです。

  • 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 に答える
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 に答える