2

I am implementing a tracker for my system which will track user active time and idle time. I am not able to get idle time from the following code. Can any body help me?

My code is as follows:

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;

namespace Ultimate_Tracker
{
    public partial class Form1 : Form
    {
        private bool start = false;
        private Timer timer;
        private bool toggleTrackerButton = true;
        private DateTime lastActivityTime;
        private TimeSpan trackedTime;
        private TimeSpan idleTime;

        // Hook handles
        private IntPtr mouseHookHandle;
        private IntPtr keyboardHookHandle;
        private bool idleTimeAlertShown=true;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
        public Form1()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
        {
            InitializeComponent();
            timer = new Timer();
            int interval = GetRandomInterval();
            if (interval != 0)
            {
                timer.Interval = interval;
            }
            else
            {
                timer.Interval = 1;
            }
            timer.Tick += timer1_Tick;
            lastActivityTime = DateTime.Now;

            // Start the mouse and keyboard hooks
            StartHooks();
        }

        // Import the user32.dll
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        private const int WH_MOUSE_LL = 14;
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_MOUSEMOVE = 0x0200;
        private const int WM_KEYDOWN = 0x0100;

        // Hook callbacks
        private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (int)wParam == WM_MOUSEMOVE)
            {
                ResetIdleTimer();
            }
            return CallNextHookEx(mouseHookHandle, nCode, wParam, lParam);
        }

        private IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (int)wParam == WM_KEYDOWN)
            {
                ResetIdleTimer();
            }
            return CallNextHookEx(keyboardHookHandle, nCode, wParam, lParam);
        }

        private void StartHooks()
        {
            using (ProcessModule module = Process.GetCurrentProcess().MainModule)
            {
                IntPtr moduleHandle = GetModuleHandle(module.ModuleName);

                // Set the mouse hook
                mouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, MouseHookCallback, moduleHandle, 0);
                if (mouseHookHandle == IntPtr.Zero)
                {
                    // Handle hook setup failure (if needed)
                }

                // Set the keyboard hook
                keyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, moduleHandle, 0);
                if (keyboardHookHandle == IntPtr.Zero)
                {
                    // Handle hook setup failure (if needed)
                }
            }
        }

        private void StopHooks()
        {
            // Unhook the mouse hook
            if (mouseHookHandle != IntPtr.Zero)
            {
                UnhookWindowsHookEx(mouseHookHandle);
                mouseHookHandle = IntPtr.Zero;
            }

            // Unhook the keyboard hook
            if (keyboardHookHandle != IntPtr.Zero)
            {
                UnhookWindowsHookEx(keyboardHookHandle);
                keyboardHookHandle = IntPtr.Zero;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            toggleTracker.FlatStyle = FlatStyle.Flat;
            toggleTracker.FlatAppearance.BorderSize = 0;
            this.MouseMove += Form1_MouseMove;
            this.KeyPress += Form1_KeyPress;
        }

        private void Form1_KeyPress(object? sender, KeyPressEventArgs e)
        {
            ResetIdleTimer();
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            ResetIdleTimer();
        }
        private void ResetIdleTimer()
        {
            if (idleTimeAlertShown)
            {
                idleTimeAlertShown = false;
                idleTime = TimeSpan.Zero;
            }
            lastActivityTime = DateTime.Now;
        }

        

        
        private void timer1_Tick(object sender, EventArgs e)
        {
            TimeSpan currentTime = DateTime.Now - lastActivityTime;
            trackedTime += currentTime;
            lastActivityTime = DateTime.Now;
            // perform some activity
            lastActivityTime = DateTime.Now;
        }


        private void toggleTracker_Click(object sender, EventArgs e)
        {

            if (!toggleTrackerButton)
            {
                // Reset the trackedTime and idleTime when starting the tracker
                trackedTime = TimeSpan.Zero;
                idleTime = TimeSpan.Zero;
                idleTimeAlertShown = true; // Set to true to show the first idle alert

                toggleTracker.BackgroundImage = Properties.Resources.btnRed;
                toggleTrackerButton = true;
                timer.Start();
            }
            else
            {
                toggleTracker.BackgroundImage = Properties.Resources.btnGreen;
                toggleTrackerButton = false;

                timer.Stop();
                MessageBox.Show("Tracked Time: " + trackedTime.ToString() + "\nIdle Time: " + idleTime.ToString());
            }
        }
    }
}

I tried to implemet tracker and get user active time and idle time. I can get active time clearly but not able to get idle time.

  • Can't you just use a [`StopWatch`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=net-7.0) and get the `Elapsed` property when you need the `TimeSpan`? – DisplayName Jul 25 '23 at 15:35
  • Please reduce your code to the minimum needed to understand your problem. We surely don't need to see how you take screenshots or change into DarkMode. Irrelevant code just makes it hard to find the relevant stuff and most people will give up early. Make it easy to help you not extra hard by just throwing everything in here. – Ralf Jul 25 '23 at 16:00
  • Hi is there any specific reason why you are not subscribing to the Application.Idle event handler? – Son of Man Jul 25 '23 at 16:58
  • @Ralf Thanks for guiding I have reduced the code. which is necessary to help one better understand. – Muhammad Shakir Jul 27 '23 at 12:12
  • @DisplayName I am new to windows form and learning it. Please let me know how can I use StopWatch and can get elapsed time. – Muhammad Shakir Jul 27 '23 at 12:13
  • @QingGuo Actually I have mostly taken help from the internet as I am new to windows form C#. can you please let me know where to subscribe in the code? – Muhammad Shakir Jul 27 '23 at 12:15
  • Still to much to follow but you say you have the active time then shouldn't the idle time not just be observation period minus active time? – Ralf Jul 27 '23 at 12:30

1 Answers1

1

Based on your response to my comment, I felt like you could do with some more context around Stopwatch - maybe this will solve your problem.

In your current approach you are manually recording (and resetting to zero) a TimeSpan.

I'd suggest using a Stopwatch instead. You can call .Restart(). to set it back to zero, and .Ellapsed to get the TimeSpan representing the time since it was last restarted.

Example using your code (I don't want to write this for you so I've simplified to only the relevant parts):

private Stopwatch idleStopwatch;

private void ResetIdleTimer()
{
    if (idleTimeAlertShown)
    {
       idleStopwatch.Restart();
    }
    Debug.WriteLine("Reset");
    Console.WriteLine("Reset");
}

private void toggleTracker_Click(object sender, EventArgs e)
{
    if (!toggleTrackerButton)
    {
        idleStopwatch.Restart();
    }
    else
    {
        MessageBox.Show($"Idle Time: {idleStopwatch.Elapsed}");
    }
}

Hope this helps :)

DisplayName
  • 475
  • 2
  • 13