Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / operating-systems / Windows

Command Prompt Gadget

3.50/5 (9 votes)
30 Jan 20079 min read 2   2.4K  
A sidebar gadget that allows you to do what you usually did in the Command Prompt Window.

Sample Image - commandpromptgadget.jpg

Introduction

This gadget tries to improve this old Windows utility - the Command Prompt - by making it accessible through the Sidebar and by making its interface more customizable, while still retaining as much of the functionality as possible. If Linux has at least a dozen Command Prompt replacements, why should Windows have only one? And even that one command prompt looks quite out of place in the new Vista Aero interface...

Analysis of possible solutions

The first and normal stage after defining the problem you want to solve is to check and see if somebody else did not solve it first. My research revealed two endeavors in this direction.

  1. Embedding a real Command Prompt

    This approach was used by Pavel Zolnikov in his CommandBar article. A short analysis of good and bad points is appropriate. This method obviously retains the greatest functionality; I would say almost all the command prompt functionality is kept, but at the cost of having to use native APIs. Although native APIs can be used in Sidebar Gadgets, (see my other gadget), a .NET only solution would be preferable for the sake of simplicity. Also, this approach does not fulfill the other goal of our project: offering an interface that can be easily customizable. Yes, the old Command Prompt is stuck with its standard font and background and that won't change any time soon.

  2. Running the process with parameters and output redirection

    Another viable option is using the .NET framework classes for running a process (we will run, of course, the famous cmd.exe ) . When we run the program we can send parameters (the commands we want to execute). The output will be kept and shown to the user. This solution, invented by Senthil Kumar in his article, offers a lot more flexibility for the interface than the previous one, but in regard with the functionality, it loses, because we cannot use features like auto completion (up / down arrows , tab, etc). Furthermore, there are lots of problems when running consecutive commands, like this:

    >> cd c:
    >> dir

    The original command prompt would list the contents of the C: drive, but our program does not. Because every command is issued to a new cmd.exe process, this prints the contents of the original folder, not of C:. This issue could be solved by making a .bat file listing all the commands given until the current one and run this script instead of just the current command; you guessed right, doing this is not very efficient.

  3. Simulating MS-DOS commands using .NET functions

    This approach does not use the original cmd.exe at all. We write the MS-DOS commands used using .NET framework functionality. Obviously there is a lot of work involved, but this can be done. The real problem is that we lose all possibility for extensibility. Let's say I have Windows powershell installed. The command prompt replacement built with this method would not be able to use any of its commands.

My approach - Input-Output redirection

Keeping in mind the goals (keep as much functionality, use .NET for UI flexibility), I searched the MSDN docs for a two way communication mechanism with command line apps. Hidden in there were instructions for input-output redirection. It seems that when starting a process it is possible to use a handler to asynchronously receive the output while at the same time to send synchronously input to the program. First, we enable redirection (commandPromptProcess being an instance of the Process class that allows running an external executable):

C#
commandPromptProcess.StartInfo.UseShellExecute = false;

Then we state that we want redirected output and set the function to receive it:

C#
commandPromptProcess.StartInfo.RedirectStandardOutput = true;
commandPromptProcess.OutputDataReceived += new DataReceivedEventHandler
                            (OutputHandler);

To specify input it's simpler, we just call:

C#
commandPromptWriter.WriteLine(MSDOScommand);

But then, how are we going to display the output? Keep in mind that the handler is invoked from another thread than the UI is, so we will have to use some inter-thread communication, like this:

C#
public delegate void UpdateTextCallback(string text);

private void UpdateText(string text)

{
outputBox.AppendText(text+System.Environment.NewLine) ;
outputBox.ScrollToCaret();
}

The ScrollToCaret function is there just to make sure that we can actually see the last line of output (it scrolls the richtextBox if necessary). The whole magic of accessing the textbox from another thread lies in the calling of the updateText function :

C#
outputBox.Invoke(new UpdateTextCallback(UpdateText), new object[] 
                            { outLine.Data }); 

So, until now we have set up input/output redirection for a process. All we need to do now is to start it, like this:

C#
commandPromptProcess.StartInfo.FileName = "cmd.exe";
commandPromptProcess.Start();

But then we discover that we still see the command prompt Window; that is perfectly logical as we have redirected just the output and input. The Windows created are still there. We want this Window to not show at all, so we write:

C#
commandPromptProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
commandPromptProcess.StartInfo.CreateNoWindow =true;

Using the code to create a gadget

Gadget are, in fact, just web pages which, in their simplest form, are HTML documents. So we will have to package the above code in a way accessible from an HTML file. We have created a Windows Forms Usercontrol and now we will make it accessible from HTML by assigning a unique ID, called GUID, that uniquely identifies it; then we need to make it visible by setting the corresponding attribute:

C#
[Guid("85BECB98-B07E-11DB-BA8B-428B56D89593")]
[ComVisible(true)]

public partial class CommandPromptUsercontrol : UserControl
{

Then, from the main gadget file we instantiate it:

C#
<object id="command"
classid="clsid:85BECB98-B07E-11DB-BA8B-428B56D89593"
 style="background-color:Transparent;"  >
</object>

What we have created is called an ActiveX control.

The transparent background trick

As you can see in the screenshot, the gadget is an icon that extends outside of the Sidebar. This is used so that you can remove the gadget without having to right click the Sidebar, clicking Properties, View list of... and clicking Remove. Just pause your mouse over it for a little while and the close button will appear. Why does the button appear only then? The answer is that Windows knows that the mouse is over the gadget when the background of the gadget file is under the mouse ( "while MouseOver" ) but our whole gadget is an ActiveX control so Windows would never show the Close button. This is why I used a background image with transparency ( .PNG supports transparency ) and left the icon corner of the image not 100% transparent; thus the mouseover event appears and so does the Close button. The corner also serves so that we can drag the gadget out of the Sidebar.

Extending the features

Drag and Drop

Because we made this simple but effective UI in Forms 2.0, we have automatically added support for Cut and Paste with standard shortcuts. Another feature I missed in the old Command Prompt is drag and drop. I wanted to drag a file to the command prompt so I can run it but it was not possible. Well, implementing this in C# is pretty simple. Make sure the destination control has AllowsDrop property set to true, and then implement these two functions:

C#
void CommandPromptUsercontrol_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(DataFormats.Text)) e.Effect = 
                        DragDropEffects.Move;
}

void CommandPromptUsercontrol_DragDrop(object sender, DragEventArgs e)
{
    commandInput.Text += e.Data.GetData(DataFormats.Text).ToString();
}

DragEnter is run when you drag something over the control and DragDrop runs when you let go of whatever you dragged over the control. Simple as that!

Highlighting

Because the small space of the Sidebar might make things pretty hard to understand I decided to implement simple highlighting to make past commands more visible in the output. As you can see in this screenshot, the command is colored blue. Also observe that the icon used for dragging is behind the Window, so it does not hinder the text visibility.

Resizable gadget

Because the space is pretty small in the Sidebar, I decided to make the gadget bigger when it is not docked. This is done with a small script written in JavaScript like this:

C#
function largeGadget()
{
   var crtWidth=System.Gadget.Settings.read("width");
   var crtHeight=System.Gadget.Settings.read("height");
   if ((crtWidth!="") && (crtHeight!=""))
   {
      resizeGadget(crtHeight,crtWidth);
      command.style.width=crtWidth-20;
      command.style.height=crtHeight;
      bkg.style.width=crtWidth;
      bkg.style.height=crtHeight;
   }
   else
   {
      resizeGadget(350,500);
      command.style.width=480;
      bkg.style.width=150;
      bkg.style.height=350;
   }
}

As you can see, the code uses the width and height from the settings dialog so you can decide for yourself how big or small you want the gadget to be when undocked. The command variable is in fact our ActiveX control's ID. The automatic resize of the control on it happens because the textBox is Docked to the bottom of the control (Docking means "slueing" the control to the corresponding border of its container) and the richtextBox is Anchored (that is, it stays at a constant distance from the desired border of its container)

Sample image

If you don't have Vista yet

You can still use this application. I made a stand alone .NET 2.0 app so you don't even have to install .NET 3.0; the 2.0 version is enough. Download the demo project and install.

Conclusion

It would be a good idea, I think, to enumerate the good and bad points of my solution:

GoodBad
  • flexibility in UI
  • pure .NET code (although exposed as ActiveX)
  • usability improvements because of always on screen location
  • most features do not require rewriting
  • allows real Windows features (drag and drop, standard copy-paste, etc.)
  • some features are lost (autocomplete) , but can be implemented manually
  • small space makes long output difficult to read. Use it undocked if it bothers you, or use the standalone app

My approach is, at least to the best of my knowledge, innovative; I did not find this way of interacting with a command line utility anywhere else and it proves a reliable solution for wrapping any console application. It makes a compromise when considering the features that it retains but these are compensated by the improvements it provides.

Bibliography and Release Notes

All materials needed to understand the specific problem are presented in the course of the article. The code provided is free for use in non-commercial applications. If you would like to use it in a commercial app (or the idea of redirection .NET was useful for the same purpose) please leave a message here. The code is my own property (comments from MSDN have been left in all entirety where needed) and all the files attached also, except the icon which I found on the DC network and made all efforts possible to identify if it is copyrighted to no prevail. To enjoy a better experience with the gadget you should use the Consolas Font. A link to where you can download it is at the top of the article. This is the best font for programmers, directly from Microsoft labs.

From the original features of the Command Prompt some don't work (tab, up arrow, etc) but some additional features are enabled by the use of .NET framework (copy-paste, drag and drop, etc).

History

  • Update 31/1/2007: Added installer, stand alone app, highlighting, resizable undocking, fixes

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here