2

newbie here, my form is monitoring Caps Lock status but is using around 50% of CPU, I think this is related to Application.Idle += Application_Idle and Application.Idle -= Application_Idle. Once I've removed those my form is not monitoring Caps Lock state, any suggestions?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CapsLockChecker
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Application.Idle += Application_Idle;
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        void Application_Idle(object sender, EventArgs e)
        {
            if (Control.IsKeyLocked(Keys.CapsLock))
            {
                label1.Text = "CapsLock is On";
                pictureBox1.ImageLocation = "C:\\Users\\user\\source\\repos\\CapsLockChecker\\CapsLockChecker\\if_Circle_Green_34211.png";
            }
            else
            {
                label1.Text = "CapsLock if Off";
                pictureBox1.ImageLocation = "C:\\Users\\user\\source\\repos\\CapsLockChecker\\CapsLockChecker\\if_Circle_Red_34214.png";
            }
        }

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            Application.Idle -= Application_Idle;
            base.OnFormClosed(e);
        }
    }
}
Ellrick
  • 21
  • 2
  • 1
    I'd use a timer for monitoring instead. A period of something sensible like 50ms or 100ms wouldn't be noticeable by the user, but will drastically reduce CPU load. BTW, thank you for putting effort into your question. It makes answering much easier. – itsme86 Dec 01 '17 at 22:55
  • `Application.Idle` is called far too often. You should be doing as little as possible in that code, and polling key status is too much. Use a timer instead; you can't possibly need to update the keyboard status that frequently. The user won't notice a delay if you're polling every quarter of a second (250ms) instead, and you'll use far less CPU. – Ken White Dec 01 '17 at 23:01
  • 1
    Even better, capture events when CapsLock is pressed. – NetMage Dec 01 '17 at 23:11
  • There isn't anything wrong with Application.Idle, it is in fact a very good way to discover the bug in the program. The problem is caused by the PictureBox.ImageLocation assignment. It assumes that it always has to reload the image, even if it is the same file. Other than burning 100% core, you should also see the memory usage of your program shoot up. Congratulations, such bugs are pretty hard to find and nobody here saw it either. Do it correctly [this way](https://stackoverflow.com/a/39183212/17034). – Hans Passant Mar 26 '18 at 08:47
  • @HansPassant Thanks Hans, I'm glad I've managed to discover something new ;) – Ellrick Jul 20 '18 at 19:48

2 Answers2

0
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        this.KeyDown += CapsLockMonitor;
        this.KeyPreview = true;
    }

    private void CapsLockMonitor(object sender, KeyEventArgs e)
    {
        if (Control.IsKeyLocked(Keys.CapsLock))
        {
            this.label1.Text = "Caps lock enabled!";
        }
        else
        {
            this.label1.Text = "Caps lock disabled!";
        }
    }
}

This appears to not chew up all my CPU, I subscribe to the KeyDown event with my custom delegate.

D. Foley
  • 1,034
  • 1
  • 9
  • 23
  • This is interesting, thanks. It's working but only if form is active, if different window is selected Caps Lock status doesn't change, do you think timer will help? – Ellrick Dec 02 '17 at 00:06
  • https://github.com/gmamaladze/globalmousekeyhook I would check this out, it does the same thing as my code except you can listen for the events outside of the application. – D. Foley Dec 02 '17 at 00:13
0

Honestly, keeping the CapsLock status constantly monitored, even when the application is idle, looks like an overkill to me. I mean, I don't doubt it's a nice feature, but I don't know if it's worth the effort for implementing it.

If your application has to warn the user whenever he is typing something while his CapsLock is turned on, the best (and simpler) approach would be showing a tooltip or a little warning box somewhere as soon as the user focuses a Control that allows text editing. Even if the code is based on WPF framework, you have an excellent example of what I'm talking about here.

In order to perform what you are looking for, you need to set up a very complex system based on Global Keyboard Hook. Following this link you can find a very nice and detailed article ("Processing Global Mouse and Keyboard Hooks in C#") describing how to accomplish this task. The link also contains a demo code written in C# that you can deploy and try.

A little excerpt:

This class allows you to tap keyboard and mouse and/or to detect their activity even when an application runs in the background or does not have any user interface at all. This class raises common .NET events with KeyEventArgs and MouseEventArgs, so you can easily retrieve any information you need.

Tommaso Belluzzo
  • 23,232
  • 8
  • 74
  • 98