0

I am working on an Windows Form that will run to Lock the Desktop after x minutes of User's inactivity.

I tried GetLastInputInfo() but it only detect the User's inactivity based on Keyboard and Mouse movement only. Well, it is written on their explanation (or do I misunderstand it?).

This function is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function.

*Now I understand the definition of system-wide user input, thanks to @Garr Godfrey

And now, I want to extend the functionality by detecting a running media player (From web browser or Window's media player).

For example:

  • User "A" did nothing in his/her Desktop, so the Timer start
  • But if he/she watch a youtube video or a video through the Windows's media player, The timer is restarted to 0 again until the player stop playing any media.

My current Code is like this

    public class Form1 : Form
    {
        private struct LASTINPUTINFO
        {
            public uint cbSize;
            public uint dwTime;
        }

        [DllImport("user32.dll")]
        static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        public CancellationTokenSource ctsCheckInactive;
        public int InactivityCheckTimeGap = 1 * 1000;
        public int InactiveThresholdInS = 15 * 60 *1000; //15 minute

        public Form1()
        {
            InitializeComponent();
            CheckInactivity();
        }

        public void CheckInactivity()
        {
            ctsCheckInactive = new CancellationTokenSource();
            var t = System.Threading.Tasks.Task.Run(async () => await CheckInactivityRoutine(ctsCheckInactive.Token), ctsCheckInactive.Token);
        }

        private async Task CheckInactivityRoutine(CancellationToken ct)
        {
            while (true)
            {
                var time = GetLastInputTime();
                //Here I want to add Media Player Detection
                //if(DetectMediaPlayer() == true){
                //  time = 0
                //}
                
                //For debugging 
                Console.WriteLine(time + " Sec");

                If(time > InactiveThresholdInS){
                  //Lock the PC or show screen saver
                }

                ct.ThrowIfCancellationRequested();
                await System.Threading.Tasks.Task.Delay(InactivityCheckTimeGap);
            }
        }

        static uint GetLastInputTime()
        {
            uint idleTime = 0;
            LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
            lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
            lastInputInfo.dwTime = 0;

            //Gets the number of milliseconds elapsed since the system started.
            uint envTicks = (uint)Environment.TickCount;

            if (GetLastInputInfo(ref lastInputInfo))
            {
                uint lastInputTick = lastInputInfo.dwTime;
                idleTime = envTicks - lastInputTick;
            }

            return ((idleTime > 0) ? (idleTime / 1000) : 0);
        }
}

Does anyone know how to achieve that?

My program is minimized to windows tray and always running after the user logged in. And it has the ability like this.

Program on the system tray


EDIT: Added pseudo code

MH Rahman
  • 81
  • 8
  • why not just implement it as a screensaver? – Garr Godfrey Mar 22 '22 at 04:53
  • Yes that's make sense. But we want to lock the Desktop for the sake of their data's safety. So once they are not idle, they have to login from the locked state – MH Rahman Mar 22 '22 at 05:04
  • I thought windows can already do this, out of the box? – Caius Jard Mar 22 '22 at 06:06
  • @CaiusJard Yes I know. I just want to make it simpler. So the user doesn't have to go to Control Panel and set it up. Instead, User just set the timeout through this program and it has the similar function (since I haven't found the way to change that timeout value programmatically) – MH Rahman Mar 22 '22 at 06:10
  • 1
    You mean a solution that configures windows screen saver would suffice? Does https://stackoverflow.com/questions/28252082/how-to-change-screen-saver-timeout-and-screensaverissecure-in-windows-using-c-sh help? – Caius Jard Mar 22 '22 at 06:16
  • @CaiusJard THANK YOU. this is exactly what we need. However, since I don't know the pros and cons so maybe i'll bring this solution to the meeting table. – MH Rahman Mar 22 '22 at 07:36
  • Sounds like you are reinveting the wheel, a blank screensaver should do this – Charlieface Mar 22 '22 at 12:55
  • @Charlieface Yes I know this is "Reinventing the wheel" job. That's why instead of create the similar function by myself from scratch I want to utilize the existing windows API – MH Rahman Mar 23 '22 at 00:23

1 Answers1

1

System wide user input reflects the fact that multiple users could be logged in to the same computer and have different desktops and sessions. Some users may be connected via Remote Desktop, or simply there could be multiple users logged in and you are switching between them.

Detecting media playing is problematic. You may be able to use one of the audio interfaces and measure output volume levels, but that's a major PITA. Detecting video is likely impossible (short of packet sniffing to look for packets coming from known streaming sites, hardly worthwhile or bullet proof).

Instead, why not implement a screen saver executable? The media programs disable screen savers while they run, and Windows handles launching it when it should. You can do a lot of stuff from the EXE itself. You could even use built in screensavers and just apply the setting to lock screen when resuming.

EDIT: A screensaver is really any executable file, renamed to be .scr. Windows will execute it based on the system screensaver settings. There are a few extra windows messages to handle, so I put examples in comments.

Garr Godfrey
  • 8,257
  • 2
  • 25
  • 23
  • Oh wait wait. Can you share me the example of screen saver program? Looks like I misunderstand your Comment and I want to make sure whether your idea is correct – MH Rahman Mar 22 '22 at 05:05
  • 1
    [SetThreadExecutionState](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate) -- [PowerCreateRequest](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powercreaterequest) (Media Players / Browsers already call these on their own) – Jimi Mar 22 '22 at 05:06
  • here is a sample from MSFT https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms686421(v=vs.85) – Garr Godfrey Mar 22 '22 at 05:08
  • @Jimi Thanks for your input. But that's the reverse of what Am I wanted to do. I want this program to actively listening for inactivity. Not telling the Desktop to "Don't Lock your pc for x minutes" – MH Rahman Mar 22 '22 at 05:10
  • this might be better. https://www.codeproject.com/Articles/4809/How-to-develop-a-screen-saver-in-C – Garr Godfrey Mar 22 '22 at 05:10
  • this is a c++ example, the only kind I've done https://stackoverflow.com/questions/5165133/how-can-i-write-a-screen-saver-for-windows-in-c – Garr Godfrey Mar 22 '22 at 05:12
  • @GarrGodfrey First of all, thanks for your explanation. I really appreciate it, and honestly I haven't consider about that Clever solution. But Sorry for my incompetence, I don't understand why you suggest me ScreenSaver as solution. Because the project didn't show any code that "detect user's Inactivity". Or am I missing something? – MH Rahman Mar 22 '22 at 05:49
  • @GarrGodfrey After its renamed to *.scr, it will be treated like System's screensaver settings (just like what you explain). I'll bring this to our meeting table. Thanks for your input – MH Rahman Mar 22 '22 at 06:18
  • 1
    You didn't read what's in those links. *[...] Lock the Desktop after x minutes of User's inactivity*: that's what the System already does -- *watch a youtube video or a video through the Windows's media player, The timer is restarted to 0*: that's what the System already does, since it allows applications to call those functions and, guess what, restart the idle Timer, which suspends and (optionally) locks the System after a given time -- So, your app is meant to implement something that is already a standard System functionality. – Jimi Mar 22 '22 at 10:39
  • Nonetheless, your app can call those functions to restart the System Timer and thus stop the suspend / lock procedure when a Media Player is running (or any other condition). Keeping in mind that Media Players already do that: your System won't suspend if the User Input is Idle but video/audio is playing. – Jimi Mar 22 '22 at 10:39