Click here to Skip to main content
15,887,289 members
Articles / Programming Languages / C#
Article

Display the Output from Command-line Process on your Desktop (A GeekTool Clone)

Rate me:
Please Sign up or sign in to vote.
3.11/5 (7 votes)
15 Feb 2007CPOL6 min read 41.2K   494   30   5
The trials and tribulations of creating a GeekTool clone
GeekTool

Introduction

There seem to be a lot of Windows applications which will display certain information to your screen (memory usage, CPU utilization, etc.), but they are either chock-full of eye candy (widgets?) or are limited by the programmer to what they want to display; I wanted something a little more flexible. One of my favorite applications for OS X is GeekTool which takes the output from a command-line process and, in essence, writes it to your desktop on a recurring basis. I figured it wouldn't be that difficult of an app to replicate for Windows users so I decided to do just that. And I stole their name, too, which is pretty lame, but I couldn't think up anything catchier.

Usually I use SysInternals' excellent command-line PsList part of the also excellent PsTools) to output a continuously updated list of processes onto my desktop, but many other command-line applications should work just fine (tail -f also works pretty well and is included in many Unix ports for Win32). I am sure there are other command-line utilities that could generate some great content for your backdrop. I have had issues with any command that continually polls and writes out to standard output (for example, the top command) because GeekTool waits for the process to exit to grab what it wrote to standard out and those processes don't ever "exit" (until, of course, someone hits Ctrl-C).

One of the interesting features that I thought would also be helpful is the ability to use regular expressions to only display the content that I actually wanted to see. It is almost like a filter on the output from the process. I have used it to only show certain content that I am interested in or even rearrange the content into a certain order (named groups are supported). Say for example that you match certain content that looks like the following:

PidWorkingSetCPU
4040Kb55%
6790Kb3%

You could rearrange that information when displayed through GeekTool with named groups very easily. However, if you don't know how to write regex, you don't need to. The default is to just show all of the output from the process.

Note: You will need to take a look at the XML file that stores all of the settings for this application to work for you. It's not hard to figure it out, but I haven't built a frontend for the settings (and I may never do so). There is also a Readme that explains what all of the settings are.

Sample Code

Because this is a whole program, I can't really talk about whole pieces of functionality, but there are bits and pieces which I had to either search a lot of CodeProject for or look around Google. Because this is a weird hybrid application, I think I was trying to do something that most people don't really think about. I am using Windows Forms, but I didn't want them to show up in the taskbar or while a user ALT-TABed. I didn't want any sort of borders or resizing capability. I also wanted GeekTool to always stay "behind" other applications. Some of the things, in particular, that I thought might be interesting are listed below.

Normally the user can drag GeekTool into different locations on the screen, but there is also an option to "lock" the application to a particular location, so that it won't move and no button-clicks will register. This is done through the Win32 API.

C#
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName); 

[DllImport("User32.dll")]
static extern int SetParent(int hWndChild, int hWndNewParent); 

int pWnd = FindWindow("Progman", null);
int tWnd = this.Handle.ToInt32();
SetParent(tWnd, pWnd);

To hide Windows Forms from users when they ALT-TAB, you also use the Win32 API.

C#
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 

SetWindowLong(this.Handle, GWL_EXSTYLE, (GetWindowLong(this.Handle, GWL_EXSTYLE) | 
    WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW);

Getting output from a command-line process is pretty basic and is all over CodeProject and the Internet, but I will rehash it here.

C#
Process process = new Process();

//Set some ProcessStartInfo properties.
process.processStartInfo.CreateNoWindow = true;
process.processStartInfo.RedirectStandardError = true;
process.processStartInfo.RedirectStandardOutput = true;
process.processStartInfo.UseShellExecute = false;

process.Start();
process.WaitForExit();

using (System.IO.StreamReader sr = process.StandardOutput)
{
     string stdOutput = sr.ReadToEnd();

     if (!string.IsNullOrEmpty(stdOutput))
     {
           // Write standard output to screen here...
     }
}

To override the default behavior of reading the App.config file, you have to add a section like the following to your App.config file.

XML
<configSections>
    <section name="instances" type="GeekTool.InstancesHandler, GeekTool" />
</configSections>

Then, you need to create a class that derives from IConfigurationSectionHandler that includes a virtual method called Create().

C#
public virtual object Create(object parent, object configContext, XmlNode section)
{
    List<instance /> list = new List<instance />();

    foreach (XmlNode node in section.SelectNodes(instanceConst))
    {
        XmlNodeReader xmlNodeReader = new XmlNodeReader(node);
        XmlSerializer serializer = new XmlSerializer(typeof(Instance));
        Instance instance = (Instance)serializer.Deserialize(xmlNodeReader);

        list.Add(instance);
    }

    return list;
}

Obviously I also created an Instance class that contained all of the settings information for each "instance" of GeekTool that I created. Then, in the Main function, I generate a list of the instances that are defined.

C#
List<instance /> instances = (List<instance />)ConfigurationManager.GetSection("instances");

And then I can loop over my list of instances and create a new Windows Form for each (the settings defined in the XML are passed into the form).

C#
foreach (Instance instance in instances)
{
    Main main = new Main(instance);
    main.Show();
}

Errata

The application works great except for one case, which I cannot seem to get around. When GeekTool is running and a user either logs off, restarts, or shuts down, occasionally, especially when GeekTool is configured to start a process quick enough, a message pops up and says: "The application failed to initialize properly (0xc0000142). Click on OK to terminate the application." Then, the user can't actually shut the computer down without clicking a bunch of OK buttons. I have scoured Google looking for an answer, but to no avail. I think it happens when Windows is telling the system that it should start killing all of its processes in anticipation of shutting down and GeekTool tries to start up a new process at the same time. So, I spent a long time playing with WM_QUERYENDSESSION messages and the SessionEnding system event trying to:

  1. stop the timer that fires that starts up new processes, and
  2. wait for any process of mine still running to safely finish.

However, I couldn't figure out a way to fix the issue and I would be very happy if anyone on The Code Project (where I always see tons of great code and good ideas) has an idea about what the issue could be or how to fix it. Regardless of that, I usually run GeekTool all day at work with no issues and two instances (one shows all of the processes on my system and one tails a log file).

Conclusion

Any other bug fixes or ideas for enhancements would be great. It does what I want it to, but I would be glad to add any other features that anyone thinks would be helpful. I do have a project on code.google.com if you want to grab the latest source from svn or look at the other issues I have designated there.

Updates

0.5.2

I uploaded some new code to fix some small issues. When a form was locked and it detected a mouse click, it would disappear and never show back up. Also, there was a bug when a form was moved, the content would not refresh. Both bugs have been fixed.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDual screens? Pin
WildRide20-Feb-07 15:47
WildRide20-Feb-07 15:47 
AnswerRe: Dual screens? Pin
adparadox021-Feb-07 4:48
adparadox021-Feb-07 4:48 
GeneralRe: Dual screens? Pin
WildRide22-Feb-07 12:29
WildRide22-Feb-07 12:29 
GeneralGreat work :) Pin
nonameatall15-Feb-07 10:34
nonameatall15-Feb-07 10:34 
GeneralRe: Great work :) Pin
adparadox016-Feb-07 4:33
adparadox016-Feb-07 4:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.