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:
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.
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.
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
static extern int SetParent(int hWndChild, int hWndNewParent);
int pWnd = FindWindow("Progman", null);
int tWnd = this.Handle.ToInt32();
To hide Windows Forms from users when they ALT-TAB, you also use the Win32 API.
[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.
Process process = new Process();
process.processStartInfo.CreateNoWindow = true;
process.processStartInfo.RedirectStandardError = true;
process.processStartInfo.RedirectStandardOutput = true;
process.processStartInfo.UseShellExecute = false;
using (System.IO.StreamReader sr = process.StandardOutput)
string stdOutput = sr.ReadToEnd();
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.
<section name="instances" type="GeekTool.InstancesHandler, GeekTool" />
Then, you need to create a class that derives from
IConfigurationSectionHandler that includes a virtual method called
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);
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.
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).
foreach (Instance instance in instances)
Main main = new Main(instance);
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:
- stop the timer that fires that starts up new processes, and
- 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).
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.
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.