Introduction
This program was written to allow easy progress or status reporting of another programs activities.
It operates on the premise that the program being monitored will write numbered status messages to a text file.
It uses a FileSystemWatcher
to detect changes in the file, and reads the messages and displays them in a dialog. I first tried the program where the main program would just write one message at a time to the message text file. I quickly found that was fragile as really fast operations could not be written fast enough to the file. So I tried having the main program keep a list of the last 20 messages, and try writing to file each time
a new message was generated. If a few writes failed, it is no problem as the next ones will have the messages that got missed. The watcher program reads the file each time it changes, and filters out past messages by their number, then displays the new ones. The reporting dialog closes after a time delay, or if the message text file is deleted. Those details are what make this tool a bit more valuable. It allows you to take a program written in any other language, maybe one that does not easily support multi-threading, and dependably see a progress report of some activity.
Background
I wrote this to help with programs written for Autodesk's AutoCad program. Tools that displayed progress bars well on Windows XP, acted differently on Windows 7, so I had to do something to deal with the UI "blocking" going on for long processes. The AutoCad tools are written in AutoLisp, and I specifically wanted an external process doing the reporting, so no chance of blocking whatsoever.
Using the Code
To use the status watcher, you will run the StatusByFileWatch.exe as an external process. It takes two command line arguments, in this order:
- the full text file filename
- the delay in microseconds, for the dilaog to close on its own when no activity happens
The demo program is written in C# but the principles here apply to any language. In any program that needs its messages to be watched, set up variables for:
- full text filename
- StatusByFileWatch.exe full filename
- message buffer list
- buffer length
string _exeLoc =
@"C:\+Storage\Programming\DotNet\StatusByFileWatch\bin\Release\StatusByFileWatch.exe";
string _txtFilename = Environment.GetEnvironmentVariable("TEMP") + @"\Logfile for Demo.txt";
List<string> _msgs = new List<string>();
int _bufflen = 20;
Then, wherever you have a process that want to have messages watched, you will:
- delete the
textfile
- clear the message list variable
- write a new message to the text file using the
writeStatusMsg
function - start the StatusByFileWatch.exe with desired arguments
- use
WriteStatusMsg
to add messages as some process proceeds - delete the text file when done displaying the messages (or do nothing and let the dialog close on its own based on the close delay argument
private void button1_Click(object sender, EventArgs e) {
try {
File.Delete(_txtFilename);
}
catch { }
_msgs = new List<string>();
writeStatusMsg("Starting Loop, be patient....");
Thread.Sleep(2000);
try {
System.Diagnostics.Process.Start(_exeLoc, "\"" + _txtFilename + "\" 7000");
}
catch { }
for (int i = 1; i < 30; i++) {
writeStatusMsg("Doing Loop " + i.ToString() + " be patient....");
Thread.Sleep(500);
}
try {
File.Delete(_txtFilename);
}
catch { }
}
Notice that this automatically trims off the first message if the message list length is exceeded.
private void writeStatusMsg (string msg) {
if (_msgs.Count >= _bufflen)
_msgs.RemoveAt(0);
_msgs.Add(_msgIndex.ToString() + "," + msg);
_msgIndex++;
try {
TextToFile(_txtFilename, _msgs.ToArray());
}
catch { }
}
The TextToFile
function is in the demo code.
Every language will have tools for starting an external EXE, and writing to and deleting text files, so this pattern can be adopted to any language.
Points of Interest
One very important point is that the watcher must read the text file in a way that does not prevent the program being watched, from writing to the file. This function does that nicely:
public static bool TextFiletoList(string fName, out List<string> fileLines) {
fileLines = new List<string>();
if (File.Exists(fName)) {
FileStream fs = new FileStream(fName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (StreamReader sReader = new StreamReader(fs)) {
string text = sReader.ReadLine();
while (text != null) {
fileLines.Add(text);
text = sReader.ReadLine();
}
}
}
if (fileLines.Count > 0) { return true; }
else { return false; }
}
The line that makes the FileStream
is critical.
There are many improvements and customizations that could be done, such as a progress bar in the message dialog. If you established certain keywords in the messages, you could do all kinds of things besides just show messages. Take that to the extreme and you are essentially serializing what you want the state of the dialog to be, and the dialog updates each time the state is posted.
The tricky part about that is mixing it with other languages that may not have deserialization functions waiting to go. I had to write my own for AutoLisp, because the default way of talking between AutoCad and .NET (called a resultbuffer
...) does unkind things to your data as it passes back and forth.
This program does not get complicated though, just uses simple numbered messages in the text file.
History
- 18th October, 2012: First release