Click here to Skip to main content
15,884,099 members
Articles / Desktop Programming / Windows Forms

ShellControl - A console emulation control

Rate me:
Please Sign up or sign in to vote.
4.61/5 (44 votes)
26 Feb 2005MIT4 min read 186.4K   6.7K   94   52
A .NET Control that emulates a command line UI

Screenshot

Introduction

Here is a good question for the CodeProject poll. How many of us have wanted to write an interpreter for a language of our own? I'm sure that would include most of us. All of us who actually tried to write one would have written it as a command line application. What if I wanted to embed it in a Windows application? Out of that question was born this control, which I call the ShellControl. It has nothing to do with the OS shell, although it can be used as a UI for one. In fact, the sample application does just that, it gets commands from the user, uses cmd.exe to run it, and displays the results in the UI. Think of it as the command tool window in Visual Studio.

What it is?

The ShellControl is a .NET User Control that you can embed directly in your Windows Forms applications. It emulates a command line UI, like the DOS shell. It is pretty basic in functionality. It provides the following features:

  • Prompt - It provides a prompt, which can be changed anytime. It works like the prompt we are used to see in the DOS shell, i.e., it can't be erased out.
  • Command History - It maintains a history of the commands executed so far. The up/down arrow keys can be used to move up/down the history just like the DOS shell.
  • Autocompletion - It provides auto completion for the last command. The right arrow key can be used for this purpose.
  • All other features of a standard TextBox, because the ShellControl in reality is just a modified TextBox, it provides all the normal functionality of the standard Windows TextBox. This includes using the mouse to select text, copy/paste, undo/redo etc.

How to use it?

Pretty simple. Add the control like any normal User Control (Right click on the ToolBox, click Add/Remove Items and Browse to ShellControl.dll). Subscribe to the CommandEnteredEvent. Do whatever you want with the command text passed in as part of the CommandEnteredEventArgs object. Here is some sample code:

C#
public Form1()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();
    shellControl1.CommandEntered += new
         UILibrary.EventCommandEntered(shellControl1_CommandEntered);
}
C#
void shellControl1_CommandEntered(object sender, 
                    UILibrary.CommandEnteredEventArgs e)
{
    string command = e.Command;
    if (!ProcessInternalCommand(command))
    {
        ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
        startInfo.Arguments = "/C " + e.Command;
        startInfo.RedirectStandardError = true;
        startInfo.RedirectStandardOutput = true;
        startInfo.UseShellExecute = false;
        startInfo.CreateNoWindow = true;
        Process p = Process.Start(startInfo);
        string output = p.StandardOutput.ReadToEnd();
        string error = p.StandardError.ReadToEnd();
        p.WaitForExit();
        if (output.Length != 0)
            shellControl1.WriteText(output);
        else if (error.Length != 0)
            shellControl1.WriteText(error);
    }
}

The code above gets the command text and executes it by starting a new cmd.exe process. It then writes back the result to the control. It can't get any simpler, can it?

Other Properties And Methods

Besides the event, the control exposes the following properties and methods:

Properties

  • Prompt - Use it to get/set the prompt displayed in the control.
  • ShellTextForeColor - Set/Get the color of the foreground text.
  • ShellTextBackColor - Set/Get the background color.
  • ShellTextFont - Set/Get the font of the console text.

Methods

  • WriteText(string text) - Use it to write text to the ShellControl. You need to remember that the control writes the prompt on the display after every call to WriteText, so if you are writing multiple lines, be sure to append them to a single string before calling WriteText.
  • Clear - Clears the console, leaving just the prompt.
  • GetCommandHistory - Returns a string array of the commands entered so far.

How it works?

The ShellControl is a class derived from UserControl. All it does is wrap ShellTextBox, which derives from TextBox. Two questions here.

  1. Why not directly expose ShellTextBox?

    Because that would expose all properties of the TextBox, including MultiLine, Text, MaxLength etc. And that would be disastrous because the ShellControl needs to tightly control the addition of text to make sure that nothing funny happens. For e.g., the ShellControl needs to make sure that you can't modify text that's above the current line, it needs to ensure that you can't erase the prompt and so on..

  2. Why derive from TextBox, why not aggregate it?

    Because ShellTextBox needs to override WndProc. And that's necessary to make sure that Cut/Paste/Clear from the Context menu don't do anything silly.

The overridden WndProc looks like this:

C#
protected override void WndProc(ref Message m)
{
    switch(m.Msg)
    {
        case 0x0302: //WM_PASTE
        case 0x0300: //WM_CUT
        case 0x000C: //WM_SETTEXT
            if (!IsCaretAtWritablePosition())
                MoveCaretToEndOfText();
            break;
        case 0x0303: //WM_CLEAR
            return;
    }
    base.WndProc(ref m);
}

As you can see, WM_CLEAR has no effect, while Cut/Paste are made to operate on the text currently at the prompt.

For controlling key presses, the ShellTextBox subscribes to the KeyPress and KeyDown events of the TextBox. The KeyPress event handler handles backspace and Enter keys while other keys are handled by the KeyDown event handler. A utility class, CommandHistory, maintains the list of commands entered as well as the current position in that list. The up/down arrow keys use information in that list to display the command history. That section looks like this:

C#
private void ShellControl_KeyDown(object sender, KeyEventArgs e)
{
    // If the caret is anywhere else, set it back
    // when a key is pressed.
    if (!IsCaretAtWritablePosition() &&
              !(e.Control || IsTerminatorKey(e.KeyCode)))
    {
        MoveCaretToEndOfText();
    }
    
    // Prevent caret from moving before the prompt
    if (e.KeyCode == Keys.Left && IsCaretJustBeforePrompt())
    {
        e.Handled = true;
    }
    else if (e.KeyCode == Keys.Down)
    {
        if (commandHistory.DoesNextCommandExist())
        {
            ReplaceTextAtPrompt(commandHistory.GetNextCommand());
        }
        e.Handled = true;
    }
    else if (e.KeyCode == Keys.Up )
    {
        if (commandHistory.DoesPreviousCommandExist())
        {
            ReplaceTextAtPrompt(commandHistory.GetPreviousCommand());
        }
        e.Handled = true;
    }
    else
    {
        // Some more code here..
    }
}

When the enter key is pressed, the ShellTextBox gets the text currently at the prompt and calls FireCommandEntered on ShellTextControl, which in turn fires the CommandEntered event. That's all there is to it.

Conclusion

Although I'm fairly comfortable in C#, this is my first attempt at creating UserControls. I know this is not going to be the most downloaded control in CodeProject, but I feel it's still useful for certain situations where you want to emulate the command line.

Feedback, comments are welcome.

History

Feb 17 2005 - Initial submission.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer Atmel R&D India Pvt. Ltd.
India India
I'm a 27 yrs old developer working with Atmel R&D India Pvt. Ltd., Chennai. I'm currently working in C# and C++, but I've done some Java programming as well. I was a Microsoft MVP in Visual C# from 2007 to 2009.

You can read My Blog here. I've also done some open source software - please visit my website to know more.

Comments and Discussions

 
Questionvery useful Pin
Southmountain30-Apr-22 17:50
Southmountain30-Apr-22 17:50 
QuestionConsole.ReadLine Pin
filmee2416-Apr-16 6:33
filmee2416-Apr-16 6:33 
GeneralRe: Console.ReadLine Pin
PIEBALDconsult16-Apr-16 6:42
mvePIEBALDconsult16-Apr-16 6:42 
GeneralRe: Console.ReadLine Pin
filmee2417-Apr-16 3:58
filmee2417-Apr-16 3:58 
QuestionCan I change a caret style (like linux terminal) ?? Pin
Tran Alex (Official)6-Apr-16 19:20
Tran Alex (Official)6-Apr-16 19:20 
Questioncould you add an multiline mode? Pin
filmee2417-Nov-15 6:22
filmee2417-Nov-15 6:22 
SuggestionCommandCompletion Pin
Member 293819220-Jan-15 19:12
Member 293819220-Jan-15 19:12 
GeneralThank you Pin
jiri7-Sep-14 21:15
jiri7-Sep-14 21:15 
QuestionThank you. Pin
Derek Viljoen14-Mar-14 6:53
Derek Viljoen14-Mar-14 6:53 
QuestionThanks for the control Pin
KathyKathyKathy5-Feb-14 9:40
KathyKathyKathy5-Feb-14 9:40 
Generalbug report Pin
shenshenaini1-Jul-09 18:22
shenshenaini1-Jul-09 18:22 
GeneralThanks for your work. Pin
xry200627-Mar-09 2:02
xry200627-Mar-09 2:02 
GeneralAdding Text Before The Prompt. Pin
Abydosgater28-Feb-09 3:22
Abydosgater28-Feb-09 3:22 
GeneralBug: Text flickes. Pin
Liu Xia20-May-08 20:21
Liu Xia20-May-08 20:21 
GeneralUseful Control Pin
pwhe2318-Feb-08 15:50
pwhe2318-Feb-08 15:50 
GeneralTry running another application! Pin
simblack3-Aug-07 5:02
simblack3-Aug-07 5:02 
GeneralRe: Try running another application! Pin
Ed.Poore22-Aug-07 9:04
Ed.Poore22-Aug-07 9:04 
GeneralWriting Commands Pin
Lava0411-Jun-07 23:11
Lava0411-Jun-07 23:11 
GeneralBug Pin
62316e1-Jun-07 7:28
62316e1-Jun-07 7:28 
Questionproblem with Cygwin Pin
jasmell26-May-07 1:22
jasmell26-May-07 1:22 
Generalproblem with change directory Pin
sars28-Feb-07 12:28
sars28-Feb-07 12:28 
GeneralNew feature Pin
Herbert Sauro26-Jul-06 15:05
Herbert Sauro26-Jul-06 15:05 
GeneralRe: New feature Pin
S. Senthil Kumar26-Jul-06 16:36
S. Senthil Kumar26-Jul-06 16:36 
GeneralRe: New feature Pin
S. Senthil Kumar2-Aug-06 6:53
S. Senthil Kumar2-Aug-06 6:53 
I whipped up a quick build for you with command completion through the tab key. Download it from http://senthil.thecoder.googlepages.com/ShellControl.zip[^] and give it a whirl.

Regards
Senthil
_____________________________
My Blog | My Articles | My Flickr | WinMacro

GeneralRe: New feature Pin
Herbert Sauro2-Aug-06 7:42
Herbert Sauro2-Aug-06 7:42 

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.