2

I have WinForms App where I am using the code in this following Post to check the InActivity Status of my app (Please see the accepted answer in the post). InActivity In WinForms. Once the app reaches inactivity its stopping the inactivity monitor. But then I want to restart the time once the user logs in.

So I have a notification mechanism when the user logs in and I am calling the start timer method again. I get the Started Monitor Message but the app never tracks inactivity and I don't get Timer reporting app is InACTIVE message at all. Please help.

 public static System.Windows.Forms.Timer IdleTimer =null;
    static int MilliSeconds = 60000;
    static void Main(string[] args)
            {
                    f = new GeneStudyForm(true, arguments.SystemTimeOutFolder, arguments.SystemTimeOutFile, StartInActivityMonitor);
                    int x = StartInActivityMonitor();
            }

        public static void StartInActivityMonitor()
        {
            IdleTimer = new Timer();
            LeaveIdleMessageFilter limf = new LeaveIdleMessageFilter();
            Application.AddMessageFilter(limf);
            IdleTimer.Interval = MilliSeconds;    //One minute; change as needed
            Application.Idle += new EventHandler(Application_Idle);
            if (IdleTimer != null)
            {
                MessageBox.Show(IdleTimer.Interval.ToString());
            }
            IdleTimer.Tick += TimeDone;
            IdleTimer.Tag = InActivityTimer.Started;
            MessageBox.Show("starting");
            IdleTimer.Start();
        }

        static private void Application_Idle(object sender, EventArgs e)
                {
                    if (!IdleTimer.Enabled)     // not yet idling?
                        IdleTimer.Start();
                }

                static private void TimeDone(object sender, EventArgs e)
        {
            try
            {
                MessageBox.Show("Stopped");
                IdleTimer.Stop();   // not really necessary
                f.MonitorDirectory();
                f.UpdateInActivityStatus();
                IdleTimer.Tick -= TimeDone;
                Application.Idle -= new EventHandler(Application_Idle);
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.InnerException + ex.Data.ToString());
            }
        }

Here is my GeneStudyForm

public partial class GeneStudyForm
{
GeneStudySystemTimeOutIO GeneStudyIO;
        Func<int> StartTimer;
//Passing the StartInActivityMonitor Method as Func Delegate
public GeneStudyForm(bool isStandalone, string TimeOutFolder, string TimeOutFile, System.Func<int> MyMethod)
{
GeneStudyIO = GeneStudySystemTimeOutIO.GetInstance(TimeOutFolder, TimeOutFile);
            UpdateActivityStatus(AppName.GeneStudyStatus, ActivityStatus.Active);
            this.StartTimer = MyMethod;
}

public void UpdateActivityStatus(AppName name, ActivityStatus status)
        {
            if (GeneStudyIO != null)
            {
                GeneStudyIO.WriteToFile(name, status);
            }
        }
        public void MonitorDirectory()
        {
            FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(GeneStudyIO.GetDriectory());
            fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
            fileSystemWatcher.Filter = "*.json";
            fileSystemWatcher.Changed += FileSystemWatcher_Changed;
            fileSystemWatcher.EnableRaisingEvents = true;
        }

        public void UnRegister(FileSystemWatcher fileSystemWatcher)
        {
            fileSystemWatcher.Changed -= FileSystemWatcher_Changed;
        }
// I am writing the inactive status to a file. So this event will fill
        private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                var root = GeneStudyIO.GetDesrializedJson();
                if (root != null && root.AllApplications != null)
                {
                    var item = root.AllApplications.Any(x => x.Status == ActivityStatus.Active.ToString());
                    if (!item)
                    {
                        if (InActivecount == 0)
                        {
                            GeneStudyAndApplicationCommon.TimeStatus = InActivityTimer.Ended;
                            MessageBox.Show("I am hiding");
                            this.Hide();
                            InActivecount++;
                        }
                    }
                    else
                    {
                        if (GeneStudyAndApplicationCommon.TimeStatus == InActivityTimer.Ended)
                        {
                            MessageBox.Show("I am showing");
                            this.Show();
                            UnRegister(sender as FileSystemWatcher);
                            UpdateActivityStatus(AppName.GeneStudyStatus, ActivityStatus.Active);
                            MessageBox.Show("Updated Status");
                            if (StartTimer != null)
                            {
                                MessageBox.Show("Starting Timer again");
                                if (StartTimer() == -1)
                                {
                                    MessageBox.Show("Couldn't start timer");
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                SystemDebugLogLogger.LogException(ex);
            }
        }
}
nikhil
  • 1,578
  • 3
  • 23
  • 52
  • Can you add the code fragment where you register the Idle event handler for the first time? Also I see you remove event handler in TimeDone. Where do you register it again? – Victor Havin Jun 08 '18 at 00:58
  • Sure I am updating the post – nikhil Jun 08 '18 at 00:59
  • One more thing: When you remove event handler, you shouldn't use new. You should supply an existing event handler. – Victor Havin Jun 08 '18 at 01:05
  • See this for removing events: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events – Victor Havin Jun 08 '18 at 01:10
  • Sure I will try supplying the event – nikhil Jun 08 '18 at 01:59
  • OK. Good luck. If you still need help - ping me. – Victor Havin Jun 08 '18 at 02:01
  • Sure Thanks Victor – nikhil Jun 08 '18 at 02:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/172726/discussion-between-nikhil-and-victor-havin). – nikhil Jun 08 '18 at 02:28
  • Hello Victor I did update my code do you see any problem with it. It still dosen't work this way. – nikhil Jun 08 '18 at 02:30
  • I looked at your code briefly. I couldn't find all necessary information to follow some logic in it. For example, your delegate 'StartTimer', which registers events is called if TimeStatus == InAvtivityTimer.Ended. This, in turn, is set if InActivityCount == 0. But I do not see where it is set to 0. There is also condition if(!item) involved, and I do not see how the item is obtained. When you start your program, does it show the message "Starting Timer again"? – Victor Havin Jun 08 '18 at 04:03
  • This is what I would recommend: Start your code with debugger. Set a breakpoint where the Idle event gets registered. Make sure it gets hit. One more thing: Your timer is set to 60 s. Make it shorter for debugging purposes. You don't want to wait 1 min just to check if event got fired. – Victor Havin Jun 08 '18 at 04:09
  • Victor I used the InActivityTimer timer because I want to recognize when the User Relogged in again. And another problem with the FileSystem Watcher is it executes twice. But that is a known functionality. But I found a different solution to my problem Victor that is using IdleCheck.GetLastInputTime(); It works really well with very little number of lines.. – nikhil Jun 08 '18 at 04:44
  • Glad it worked out for you. Thanks for sharing. – Victor Havin Jun 08 '18 at 15:13
  • No problem. Thanks for the help Victor – nikhil Jun 08 '18 at 17:22
  • Victor I was able to get above code working. I don't know how could I miss this small piece of information. I forgot using a Dispatcher in the FileChanged Changed Event when I am starting the timer. Here is the code what I am doing when starting the timer. :) System.Action action = () => { StartInActivityMonitor(); }; this.BeginInvoke(action); – nikhil Jun 11 '18 at 19:55
  • Nikhil, I am glad you got it all sorted out, but I am a little confused now. If I remember it correctly, the original question was about the Application.Idle event not being processed. The solution you posted doesn't even have this event handler in the source. Does it mean you decided not to use this event or you somehow worked around it or that it was a different problem all together? – Victor Havin Jun 11 '18 at 20:43
  • Yes Victor that is correct I decided to do a work around by using the same code I posted above. I didn't want to use the answer I posted because WinForms already has an Application.Idle event so I didn't like to use any other logic like using Unmanaged dll that I posted below.. – nikhil Jun 11 '18 at 23:11

1 Answers1

0

This soulution is quite different from what I have posted. But I could solve my problem with this. But I want to post it if it helps someone. Here is the post I am following Last User Input

I created a class called IdleCheck where I am getting LastUserInput as follows

public static class IdleCheck
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO
        {

            [MarshalAs(UnmanagedType.U4)]

            public int cbSize;

            [MarshalAs(UnmanagedType.U4)]

            public int dwTime;

        }

        [DllImport("user32.dll")]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO x);
        public static int GetLastInputTime()
        {
            var inf = new LASTINPUTINFO();
            inf.cbSize = Marshal.SizeOf(inf);
            inf.dwTime = 0;

            return (GetLastInputInfo(ref inf)) ? Environment.TickCount - inf.dwTime : 0;
        }
    }

Next in the actual Form this is my code. I am using a simple yes no message box to see if the timer can be stopped and recalled again when needed. You can apply your own locking mechanism. I want the app to time out if it is InActive for 20 seconds. Change it as needed.

public partial class Form1 : Form
    {
        Timer timer;
        const int TIMEOUT_DONE = 20000;
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Reset();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            //var ms = TIMEOUT_DONE - IdleCheck.GetLastInputTime();
            if (IdleCheck.GetLastInputTime() > TIMEOUT_DONE)
            {
                DialogResult dialogResult = MessageBox.Show("Sure", "Some Title", MessageBoxButtons.YesNo);
                if (dialogResult == DialogResult.Yes)
                {
                    Stop();
                    Reset();
                }

            }

        }

        public void Reset()
        {
            timer = new Timer();

            timer.Interval = 10000;

            timer.Tick += timer_Tick;

            timer.Start();
        }

        public void Stop()
        {
            timer.Tick -= timer_Tick;
            timer.Stop();
        }

    }
nikhil
  • 1,578
  • 3
  • 23
  • 52