Building Console+, an enhanced (hopefully) interface to windows shells

Everyday, almost, I use three different shells, Command Prompt, PowerShell, and Bash for Git. I think the interface of those shells has lots to improve. As I am spoiled by good editors like Visual Studio, TextMate, Sublime, Notepad+, (but no Vim yet), I expected the similar level of maturity and convenience.

Console+ is on github now. Please bear in mind, it is code in progress.

So, I wanted to build an interface to them. I’m not building any shell. I’m going to build just an interface. And in this journey, I start learning a few tips with WPF. A geeky joy, as Kent Beck confesses in his book, “TDD By Example”.

Resources I used

As this is a WPF application with Win API calls, I don’t have enough knowledge or experience. I am more focust on Web development (thought I don’t like being labelled as mere web developer as some windows devs believe wrongly web development is child’s play)

So, I google a lot to understand how it can possibly work. This is the list of my resources.

Adding an icon to your application

I thought I would simply put Icon=”/Resource/Icon.ico”, but it wasn’t. You have to open the property dialog and set it there.

Handling RETURN and TAB on textbox

If “AcceptsReturn” and “AcceptsTab” are on, you can’t capture those key codes in the event. They are handled within the control, and the event is not escalated. So, turn them off.

Color coding in console

Colour is an attribute of the console, and you don’t get it from StandardOutput. You have to do something with windows api, and I’m not ready get my hands dirty with win api yet. I’ll depriortise this story 🙂

DispatcherTimer

Initially, I used System.Timers.Timer to update the screen regularly, but it didn’t work. Timer runs in a separate thread, and can’t update UI thread. You get “The calling thread cannot access this object …” error. In this case, DispatcherTimer is handy, as Tick event is fired in the dispatcher thread.

_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(1000);
_timer.Tick += (o, args) =>
					  {
						  tbxConsole.Text = _console.ReadAll();
						  MoveCursorToTheEnd();
					  };
_timer.IsEnabled = true;

Sending key event to the console

I need to send user’s key input to “cmd.exe” process. In WPF key event, I get KeyEventArgs, but I need to convert it to a character, and it is not possible with the help of win api.


public class KeyHelper
{
	public enum MapType : uint
	{
		MAPVK_VK_TO_VSC = 0x0,
		MAPVK_VSC_TO_VK = 0x1,
		MAPVK_VK_TO_CHAR = 0x2,
		MAPVK_VSC_TO_VK_EX = 0x3,
	}

	[DllImport("user32.dll")]
	public static extern int ToUnicode(
		uint wVirtKey,
		uint wScanCode,
		byte[] lpKeyState,
		[Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] 
		StringBuilder pwszBuff,
		int cchBuff,
		uint wFlags);

	[DllImport("user32.dll")]
	public static extern bool GetKeyboardState(byte[] lpKeyState);

	[DllImport("user32.dll")]
	public static extern uint MapVirtualKey(uint uCode, MapType uMapType);

	public static char GetCharFromKey(Key key)
	{
		char ch = ' ';

		int virtualKey = KeyInterop.VirtualKeyFromKey(key);
		byte[] keyboardState = new byte[256];
		GetKeyboardState(keyboardState);

		uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
		var stringBuilder = new StringBuilder(2);

		int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
		switch (result)
		{
			case -1:
				break;
			case 0:
				break;
			case 1:
				{
					ch = stringBuilder[0];
					break;
				}
			default:
				{
					ch = stringBuilder[0];
					break;
				}
		}
		return ch;
	}
}

Char.MinValue

Sometimes, not often, I want to return empty character in my method. String has string.empty, but until now, I ddin’t know that Char.MinValue exists. It’s really handly. Look at this code.

public char GetCharacterFrom(Key key)
{
    if (key == Key.LeftShift)
        return Char.MinValue;

    if (key == Key.Return)
        return (char) 13;

    return GetCharFromKey(key);
}

Avalon Text Editor

You can highlight the part of text with VisualLineElement. the Rendering article has more detailed description.

With StackOverflow’s avalonedit tag, you can read through useful tips

You can download the source code, a sample application, and help file from codeproject.

Changing text color in Avalon Text Editor

Changing color of text or highlighting text is quite tricky with Avalon Text Editor. Primarily it’s because Avalon is not RichTextEditor but code editor. Text are treated as string and you put meta data on those text if you want to change the format.

You need to create your own DocumentColorizingTransformer to highlight a part of your text, and then add it to your editor’s LineTransformers collection. I found an example of custom DocumentColorizingTransformer, bud spend some time to find out how to use it.

public void UpdateConsole()
{
	tbxConsole.Document.Text = _console.ReadAll();
	tbxConsole.ScrollToEnd();
	tbxConsole.TextArea.TextView.LineTransformers.Add(new ColorizeAvalonEdit());
}

public class ColorizeAvalonEdit : DocumentColorizingTransformer
{
	protected override void ColorizeLine(DocumentLine line)
	{
		if (line.Length == 0)
			return;

		int lineStartOffset = line.Offset;
		string text = CurrentContext.Document.GetText(line);
		int start = 0;
		int index;
		while ((index = text.IndexOf("Microsoft", start)) >= 0)
		{
			base.ChangeLinePart(
				lineStartOffset + index, // startOffset
				lineStartOffset + index + 10, // endOffset
				(VisualLineElement element) =>
				{
					// This lambda gets called once for every VisualLineElement
					// between the specified offsets.
					Typeface tf = element.TextRunProperties.Typeface;
					// Replace the typeface with a modified version of
					// the same typeface
					element.TextRunProperties.SetTypeface(new Typeface(
						tf.FontFamily,
						FontStyles.Italic,
						FontWeights.Bold,
						tf.Stretch
					));
				});
			start = index + 1; // search for next occurrence
		}
	}
}


to be continued…

Building Console+, an enhanced (hopefully) interface to windows shells

Visual Studio 2010 Command Prompt Here On the Context Menu of Windows Explorer

You use keyboards and command line more and more, as you get more experienced with the tools in development. If you install git on windows, it creates “Git bash here” context menu on Windows Explorer. It is really handy, and I like it. I believe Visual Studio installation should do the same.

Well, it doesn’t, but you can create it, if you want, with the little manipulation of windows registry.

First, open regedit. Make sure you export the current settings as backup, as your mistake can paralyse your system.

Second, Create two registry keys to enable the context menu on Windows Explorer.

  • HKEY_CLASSES_ROOT\Directory\shell\vs_cmd
  • HKEY_CLASSES_ROOT\Directory\shell\vs_cmd\command

The values are like these

HKEY_CLASSES_ROOT\Directory\shell\vs_cmd

  • Name: (Default)
  • Type: REG_SZ
  • Data: Visual Studio 2010 Command Prompt Here
HKEY_CLASSES_ROOT\Directory\shell\vs_cmd\command
  • Name: (Default)
  • Type: REG_SZ
  • Data: cmd.exe /s /k pushd “%V” && “C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat” x86

Then go to windows explorer and right-click on any directory. You will see a menu “Visual Studio 2010 Command Prompt Here”

Let me explain what’s happening here.

  • “HKEY_CLASSES_ROOT\Directory\shell” is where you put settings for your windows shell.
  • cmd.exe is reachable from any directory, so you don’t have to specify the full path to execute it.
  • /s means to strip quote characters from the command_line (http://ss64.com/nt/cmd.html)
  • /k means to carry out a command but the shell remains without closing.
  • pushd let you execute cmd.exe with a given directory (http://www.hanselman.com/blog/PUSHDReminderItAutomaticallyMapsNetworkDrives.aspx)
  • vcvarsall.bat is a batch file executed when you run visual studio command prompt.

 

I also referenced the below nice posts.

but eventually, I used git’s “bash shell here” registry settings.

 

Visual Studio 2010 Command Prompt Here On the Context Menu of Windows Explorer