1

I am using my desktop application written for Windows 8 in C# to provide the readings of different device sensors on clipboard. Now, there is an external application (I dont have any control over its structure) that my application will be interacting with. Both the applications use a mutual text file as a switch. When the external app needs my readings, it renames the text file to 'SensorsTurn.txt' and puts a trigger word on the clipboard, such as ("sensors") When my application sees that the file has been named such, it reads the clipboard for trigger, collect data accordingly, puts it on clipboard and renames the text file back to 'RBsTurn.txt'. The problem is, I need my program to continuously check for the name of that file as long as it is running. One very basic way that I thought of was to throw the program into an infinite while loop. But that obviously is a very bad approach. When I see my application in the task manager, it is taking up crazy amount of CPU processing which it shouldn't. Another suggestion that I found was to make my loop a background thread, this was a little more efficient. But the time between two consecutive readings is slowed down. The guy that I am working for reported this: "The first time I try to write to the clipboard, it writes very quickly but on subsequent writes it takes about 3 seconds (I have often had two program communicate with the clipboard). I suspect that your program is not releasing the clipboard in some way. As soon as I am able to write to the clipboard, I change the file name and get your data back immediately. So the problem stems from the clipboard write.... " Here is a part of the code:

namespace sampleApplication
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    //Initialize sensor variables
    LightSensorReading _newLightSensorReading;
    LightSensor _LightSensor = LightSensor.GetDefault();

    string _pathString = System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "dummy");
    private void Form1_Load(object sender, EventArgs e)
    {
        //InitializeDevices();
        System.IO.Directory.CreateDirectory(_pathString);
        string _filePathSensors = System.IO.Path.Combine(_pathString, "SensorsTurn.txt");
        string _filePathRBsTurn = System.IO.Path.Combine(_pathString, "RBsTurn.txt");
        string _triggerString = "";
        int x = 1;
        Thread th = new Thread(() =>
            {
                while (x == 1)
                {
                    if (System.IO.File.Exists(_filePathSensors))
                    {
                        _triggerString = Clipboard.GetText();
                        switch (_triggerString)
                        {
                            case "sensors":
                                if (_LightSensor != null)
                                {
                                    _newLightSensorReading = _LightSensor.GetCurrentReading();

                                    string _deviceReading = "LightSensor" + "," + _newLightSensorReading.IlluminanceInLux.ToString();
                                    Clipboard.SetText(_deviceReading);
                                    System.IO.File.Move(_filePathSensors, _filePathRBsTurn);
                                }
                                break;
                            case "stop":
                                Clipboard.Clear();
                                System.IO.File.Move(_filePathSensors, _filePathRBsTurn);
                                Application.Exit();
                                break;
                        }
                    }
                }
            });
        th.IsBackground = true;
        th.SetApartmentState(ApartmentState.STA);
        th.Start();
    }
}
}

To cut the long story short, there are two problems: 1) how to continuously check for file name without using inefficient infinite loops? Can I define an event somehow?? 2) My program is not releasing the clipboard fast enough after using it. What could be the reason?

5 Answers5

9

Rather than constantly looking for a changed name, you might use a File System Watcher object to respond to the Renamed event. That might avoid the problem entirely.

UPDATE:

Here is a blog post (previously mentioned in comments) on the subject

Rick Liddle
  • 2,684
  • 19
  • 31
  • Very nice - I didn't know about this one. – Kohanz May 06 '13 at 14:43
  • Hey Rick, This is exactly what I am looking for. Could you please elaborate your method or provide me with some link? – Shobhan Taparia May 06 '13 at 14:47
  • PS: would this also sort the clipboard issue? – Shobhan Taparia May 06 '13 at 14:50
  • @ShobhanTaparia - [Here's a full example](http://stackoverflow.com/a/3732976/35241) of using the class to handle a variety of events in another SO answer. As far as the clipboard issue, I don't know enough about your specific situation to say. – Rick Liddle May 06 '13 at 14:51
  • @ShobhanTaparia - Along with that, [here's a blog post](http://tech.pro/tutorial/737/csharp-snippet-tutorial-using-the-filesystemwatcher-class) with a little more information beyond a straight code sample. – Rick Liddle May 06 '13 at 14:56
  • Ok, so when i try to run this: FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = Application.StartupPath + @"\RBsTurn.txt"; watcher.Renamed += new RenamedEventHandler(watcher_Renamed); watcher.EnableRaisingEvents = true; it throws an error on the third line saying, the directory name"blah blah blah" is invalid :(.. – Shobhan Taparia May 06 '13 at 15:41
  • [This](http://stackoverflow.com/questions/12798197/use-filesystemwatcher-on-a-single-file-in-c-sharp) thing worked! :D Thanks a tonne people :) – Shobhan Taparia May 06 '13 at 15:49
  • @ShobhanTaparia Glad you got it working. Did it resolve the clipboard issues as well? – Rick Liddle May 06 '13 at 20:32
  • @RickLiddle - yes, the clipboard is blazing fast now :) although, i couldn't directly access the clipboard in the Renamed event ([STA properties hiccough](http://stackoverflow.com/questions/16403609/unable-to-access-clipboard-in-filesystemwatchers-renamed-event/16403984?noredirect=1#comment23516690_16403984)). So I eventually ended up using a thread inside the event handler and setting its STA properties to get my job done. It is wroking perfectly now. Thanks :) – Shobhan Taparia May 06 '13 at 20:55
2

The RIGHT thing to do is to create a FileSystemWatcher which will raise an event when the file's name has changed (or a new file has been created - it's explained in the documentation).

When the file is created/renamed, an event will fire, which you handle and do your thing in the handler's body.

Captain Kenpachi
  • 6,960
  • 7
  • 47
  • 68
0

I would use a Timer. This way, you run at a specified interval and don't occupy the CPU otherwise. Essentially, it accomplishes what a Thread solution with Thread.Sleep does, but in a cleaner, more readable way, IMHO.

using System;
using System.Timers;

public class Timer1
{
    private static System.Timers.Timer aTimer;

    public static void Main()
    {
        // Normally, the timer is declared at the class level, 
        // so that it stays in scope as long as it is needed. 
        // If the timer is declared in a long-running method,   
        // KeepAlive must be used to prevent the JIT compiler  
        // from allowing aggressive garbage collection to occur  
        // before the method ends. You can experiment with this 
        // by commenting out the class-level declaration and  
        // uncommenting the declaration below; then uncomment 
        // the GC.KeepAlive(aTimer) at the end of the method. 
        //System.Timers.Timer aTimer; 

        // Create a timer with a ten second interval.
        aTimer = new System.Timers.Timer(10000);

        // Hook up the Elapsed event for the timer.
        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

        // Set the Interval to 2 seconds (2000 milliseconds).
        aTimer.Interval = 2000;
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program.");
        Console.ReadLine();

        // If the timer is declared in a long-running method, use 
        // KeepAlive to prevent garbage collection from occurring 
        // before the method ends. 
        //GC.KeepAlive(aTimer);
    }

    // Specify what you want to happen when the Elapsed event is  
    // raised. 
    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}

/* This code example produces output similar to the following:

Press the Enter key to exit the program.
The Elapsed event was raised at 5/20/2007 8:42:27 PM
The Elapsed event was raised at 5/20/2007 8:42:29 PM
The Elapsed event was raised at 5/20/2007 8:42:31 PM
...
 */
Kohanz
  • 1,510
  • 16
  • 35
-1

Just use an infinite loop, but at the end of your loop, add a Thread.Sleep(1000) or something like that. Then it won't use all your CPU time. Adjust the number to your desired checking frequency.

Patrick D'Souza
  • 3,491
  • 2
  • 22
  • 39
David S.
  • 5,965
  • 2
  • 40
  • 77
-2

Let your thread call a method that contains a while loop.

Max
  • 12,622
  • 16
  • 73
  • 101