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

DOSKey-like Control

Rate me:
Please Sign up or sign in to vote.
4.45/5 (9 votes)
14 Dec 2007CPOL5 min read 34.1K   379   31  
A DOSKey like Windows control with persistent MRU history
Screenshot -

F7 History Picker Dialog Box

Introduction

This project is a full featured DOSKey control, (like what is built into Windows XP Command Prompt).

We are all familiar with the DOSKey utility found in console applications or from the Command Prompt. The DOSKey control has the handy command history that is navigated via the Up/Down arrow keys. This feature prevents us from having to retype some lengthy commands over and over. Once a command has been entered, it is inserted into the DOSKey command history queue.

Did you know that DOSKey has a history window that will popup when the F7 key is hit? This gives a detailed view of where your commands are and allows you to navigate to the desired command and execute it. Also once the F7 key stroke has popped up the history window, you can hit F9 and enter the indexed selection from the history list and execute that command.

I've taken these features and placed similar functionality into this DOSKey control. I've taken all of the good features and added some enhancements to make a solid control for any command driven application. I've included a test application that simulates some command driven application that receives commands from the DOSKey control and simulates some execution engine.

Features

  • Persistent Most Recent Used (MRU) circular command queue (size is configurable).
  • Persistent MRU list - Spans application scope (history stored in the registry).
  • Command Validation - Only valid commands will be added to the MRU queue (a very nice feature).
  • Unneeded or unwanted commands can be deleted from the DOSKey History Command Picker dialog box.
  • Commands from the DOSKey History Command Picker may be routed directly to your executor or to the text box for modifications.
  • No command duplicates. If the same command is already in the list, it is moved to the Most Recent Used position.
  • Hitting the Escape key will reset the DOSKey navigation back to the Most Recent Used item in the history queue.
  • Hitting the Enter key when the DOSKey control's edit box is empty will place the Most Recent Used item into the edit box.

Using the Code

The DOSKey control is a DLL that needs to be placed in the same folder as your executable. The demo project has that already done for you. For you to place the DOSKey control in a different project, you should add it to your Toolbox. First compile the DOSKey control, then open up the Toolbox and right click, then select "Choose Item".

Choose Item from the Toolbox

Use the browse button in the "Choose Toolbox Items" dialog to navigate to DOSKey.dll, then press the Open button.

DOSKey selected in Choose Item

This will add DOSKey.dll into the Toolbox so that the control can be dragged from the Toolbox into the Form.

DOSKey now in the Toolbox

Configuring the DOSKey Control

There are some properties and events that must be defined in order for the DOSKey control to fully function properly. First we'll start off with the properties.

DOSKey's Properties

Properties

  • CtrlWidth property is controlled by the Form design view and adjusts as the control is stretched to fit into the Form in the desired location.
  • HistoryLength property is the length of the DOSKey's MRU history queue. The allowed range is set to 1 - 100 by the read only properties HistroyLengthMin and HistoryLengthMax.
  • HistroyLengthMin and HistoryLengthMax: These are read only, but they can be changed in the source code.
  • RegistryCompanyName and RegistryMruName define the location in the registry where the MRU data is to be stored. Here is the path used in this example: My Computer\HKEY_CURRENT_USER\Software\ST Howard\DosKeyMRU.

The registry is the best place to store the history since the XML and data file persistence methods are too slow and throw exceptions on rapid command entry.

Now for the events (delegates):

DOSKey's Events

Events

  • IsDOSKeyValidCommand (command validation) event, if connected, will be executed before the DOSKeyNewEntry event. This way only valid commands will be accepted and placed into the command history. If the IsDOSKeyValidCommand is not connected then all commands are assumed to be valid and are placed into the command history.
  • DOSKeyNewEntry is the event that gets fired whenever a new command has been entered in the control. This is what the owner of the DOSKey control uses to receive all the commands from the control.

Here is the code for the delegates and events:

C#
//==============================================================================
<summary />/// Command Entered Delegate:
/// This delegate is the template used for our DOSKey client to hook into our
/// call to the command entered function (DOSKeyNewEntry). This informs our
/// client that a command has been entered so the aspirate action can take place
/// in our client's application.
/// </summary />
/// <param name="str" />The command that has been entered via the Enter key.</param />
public delegate void DOSKeyHandler(string str);
/// <summary />
/// Command Entered Event:
/// This event is used to notify the owner that the enter key has been pressed.
/// The owner is expected to take the string as the command entered and implement
/// the desired actions. Typically you'd display the command that is to be
/// executed, execute the command and then display the results of the command.
///
/// NOTE: If the DOSKeyCommandValid() has been defined it will be called to
/// validate that this command is valid before executing this notification to
/// our owner. This way only valid commands will be passed along and added into
/// the MRU queue.
</summary />//------------------------------------------------------------------------------
[Category("DOSKey Configuration"),
Description("Sent when the user hits the enter key inside the textBox.")]
public event DOSKeyHandler DOSKeyNewEntry;

//==============================================================================
<summary />/// Command Validator Delegate:
/// This delegate is the template used for our DOSKKey client to hook into our
/// call to the command validator function (IsDOSKeyValidCommand). This way we
/// are smart enough to only place valid commands into the MRU command history
/// queue.
</summary />/// <param name="str" /></param />
/// <returns />The command to be validated by our owner (valid syntax)</returns />
public delegate bool DOSKeyCommandValid(string str);
/// <summary />
/// Command Validate Event:
/// This event is sent to our owner so it can determine if the command is valid.
/// If valid we'll send the command to our owner via the DOSKeyHandler() event.
/// This is used to prevent invalid or unknown commands being passed to our
/// owner.
</summary />//------------------------------------------------------------------------------
[Category("DOSKey Configuration"),
DefaultValue("true"),
Description("Used to receive notification that the command entered has valid syntax.")]
public event DOSKeyCommandValid IsDOSKeyValidCommand;

Here is the implementation using these delegates and events:

C#
//=======================================================================
// This function first tests the validity of the entered command via an
// event to our owner. If the command is valid it gets inserted into the
// command history buffer and calls the owner's delegate, if it has been
// assigned, for execution of the command.
//-----------------------------------------------------------------------
private void InsertCallDelegate(string str)
{
    if (IsDOSKeyValidCommand != null)
    {
        if (IsDOSKeyValidCommand(str) != true)
        {
            textBox.SelectAll();    // for the overwrite if user wants to
            // This command does NOT have valid syntax. Skip adding it into the queue
            return;
        }
    }

    // To be here we know that the command has been validated so add it to the queue
    InsertMruItem(str);             // insert a string into MRU history queue

    SaveDosKeyHistoryToRegistry();  // Now that we've added it to the list,
                                    // stash away the updated history list
    textBox.Focus();                // make sure we get the focus back
    textBox.SelectAll();            // for the overwrite if user wants to
    iDosKeyIndex = 0;               // start over on the Up/Down arrow counting
    if (DOSKeyNewEntry != null)
    {   // if the owner of this control has instantiated us, we can call it
        DOSKeyNewEntry(str);        // call the delegate held within our owner
    }
}

Command Validation

The default command validation in this example only allows commands that start off with: "cmd ", "mem ", "option ", and "test ". There are no other qualifications on the input. Your own data validation should be added which fully qualifies your command set.

C#
str.ToLower();
str.Trim();
if (str.StartsWith("cmd ") || str.StartsWith("option ") ||
str.StartsWith("mem ") || str.StartsWith("test "))
{
    // we have a valid command here
    return true;    // for this example we only allow 4 simple commands that are valid
}

Use of the Ctrl Key with the DOSKey Control

The history dialog box can be popped up by holding the Ctrl key down and then pressing the Up arrow key. This can be easier than hitting just the F7 key to open the history dialog box. The greatest benefit of all is when you've navigated through the history queue for a few entries and haven't found the desired item. Holding down the Ctrl key and pressing the Up arrow will open the history dialog box and have the current item in the history queue selected. This turns out to be a very nice feature when the history queue is large.

The Ctrl key is also used to pull an item from the history dialog box to the DOSKey control's edit window for tweaking. To do this, you hold the Ctrl key down and press the Enter key. This action places the item into the DOSKey's edit box for modifications. This way you're able to modify the command before executing it.

Points of Interest

Using the Registry

Reading the history from the registry.

C#
//============================================================================
// Reads the history list from the registry.
// This controls default setting the entries are:
// DosKey0, DosKey1, DosKey2, ..., DosKey(n-1) where n is the allowed length.
//  DosKey0 is the oldest entry
//  DosKey(n-1) is the newest entry
//
// Returns:
//  0 = success
//  1 = error - registry key does not exist
// -1 = error - registry key action failed (catch branch)
//----------------------------------------------------------------------------
private int ReadDosKeyHistoryFromRegistry()
{
    int iRegCnt = 0;
    string strDosKeyItem;
    RegistryKey regCurrUser_software = null;
    RegistryKey regMRU = null;
    try
    {
        regCurrUser_software = Registry.CurrentUser.OpenSubKey("Software", true);
        string strSubKey = strRegCompanyName + "\\" + strRegDosKey;

        regMRU = regCurrUser_software.OpenSubKey(strSubKey);
        if (regMRU == null)
        {
            // this key does NOT exist, just return
            regCurrUser_software.Close();
            return 1;   // key does not exist
        }
        iRegCnt = regMRU.ValueCount;

        for (int i = 0; i < iRegCnt; i++)
        {
            strDosKeyItem = string.Format("{0}{1}", strMruName, i);
            String s = (String)regMRU.GetValue(strDosKeyItem, 
                "null - Oh Crap, someone's read from an empty or corrupted list!");
            aList.Insert(0, s);
        }
    }
    catch
    {
        regCurrUser_software.Close();
        regMRU.Close();
        return -1;
    }
    regCurrUser_software.Close();
    regMRU.Close();

    return 0;
}

Saving the history queue to the registry. It is easier to delete the old key and save a new one instead of sorting the change and then saving.

C#
//===============================================================================
// Writes the history list to the registry.
// In this controls default setting the entries are:
// DosKey0, DosKey1, DosKey2, ..., DosKey(n-1) where n is the allowed length.
//  DosKey0 is the oldest entry
//  DosKey(n-1) is the newest entry
//
// returns:
//  0 = success
// -1 = error - registry key error (catch branch)
//-------------------------------------------------------------------------------
private int SaveDosKeyHistoryToRegistry()
{
    string strDosKeyItem;
    RegistryKey regCurrUser_software = null;
    RegistryKey regMRU = null;
    try
    {
        regCurrUser_software = Registry.CurrentUser.OpenSubKey("Software", true);
        string strSubKey = strRegCompanyName + "\\" + strRegDosKey;
        regMRU = regCurrUser_software.CreateSubKey(strSubKey);
        regCurrUser_software.DeleteSubKeyTree(strSubKey);       // delete the old one
        regMRU = regCurrUser_software.CreateSubKey(strSubKey);  // create a new one

        for (int i = 0; i < aList.Count; i++)
        {
            strDosKeyItem = string.Format("{0}{1}", strMruName, i);
            regMRU.SetValue(strDosKeyItem, (string)aList[(aList.Count - 1) - i], 
                RegistryValueKind.String);
        }
    }
    catch
    {
        regCurrUser_software.Close();
        regMRU.Close();
        return -1;
    }
    regCurrUser_software.Close();
    regMRU.Close();

    return 0;
}

Acknowledgements

I'd like to thank Bob Powell for the tips on how to get the DOSKey icon to show up in the Toolbox. He has a nice web site with tips and tricks that you should visit.

Thanks also to Eric Woodruff for his NDoc interface into the Sandcastle XML document builder.

License

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


Written By
Engineer
United States United States
I’ve been involved in the disk drive industry since 1986 in test development and R&D (FW - firmware). I hold bachelor degrees in Electronics, Computer Science and Mathematics.

Comments and Discussions

 
-- There are no messages in this forum --