rss
logo

I provide consulting and custom development for Natural Language Processing, Information Extraction and Search solutions.Self Picture


 learn more   get in touch 

Logo - I Build Search
Feb 19
2007

Writing a shell replacement digg

Download the source files.

This document will teach you to make your own windows shell replacement. If you don’t know what a shell replacement is, take a look at Shellfront. Before you begin, you’ll need:

  1. A working knowledge of C#.
  2. A copy of Visual Studio C# Express. [Download]
  3. A machine running Windows 2000 and above.

Creating the project

Fire up Visual C# and create a new Windows Application project. I have named it DeeShell. The first thing to do is to get rid of all the default code. Delete Form1.cs by right clicking it in Solution Explorer and selecting Delete.

You’ll be left with Program.cs. Open Program.cs and make the static void Main() method look like so:

1
2
3
4
static void Main()
{
	//Application.Run();
}

Save the project.

Setting up the Screen

Next, we’re going to set up the desktop area. Our steps will be:

  1. Hide the taskbar.
  2. Reset the desktop area.
  3. – Insert Breakpoint –
  4. Restore the desktop area.
  5. Restore the taskbar.

Add a class called WinAPI.cs to the project. This file will contain all the Win32 API that we need to P/Invoke. The best place to learn about all the functions available is at pinvoke.net.

Edit WinAPI.cs to look like so,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* DeeShell - A shell replacement for Windows
 * Pravin Paratey (February 19, 2007)
 *
 * Article: http://pravin.insanitybegins.com/articles/deeshell
 *
 * Released under Creative Commons Attribution 2.5 Licence
 * http://creativecommons.org/licenses/by/2.5/
 */
using System;
using System.Text;
using System.Runtime.InteropServices;
 
namespace DeeShell
{
	public class WinAPI
	{
		public struct RECT
		{
			public int left;
			public int top;
			public int right;
			public int bottom;
		}
 
		/// <summary>For ShowWindow</summary>
		public enum WindowShowStyle : int
		{
			Hide = 0,
			ShowNormal = 1,
			ShowMinimized = 2,
			ShowMaximized = 3,
			Maximize = 3,
			ShowNormalNoActivate = 4,
			Show = 5,
			Minimize = 6,
			ShowMinNoActivate = 7,
			ShowNoActivate = 8,
			Restore = 9,
			ShowDefault = 10,
			ForceMinimized = 11
		}
 
		/// <summary>For SystemParametersInfo</summary>
		public enum SPI : int
		{
			SPI_SETWORKAREA = 0x002F,
			SPI_GETWORKAREA = 0x0030
		}
 
		[DllImport("user32.dll", SetLastError = true)]
		public static extern bool SystemParametersInfo(uint uiAction, uint uiParam,
			ref RECT pvParam, uint fWinIni);
 
		[DllImport("user32.dll", SetLastError = true)]
		public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
 
		[DllImport("user32.dll")]
		public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
 
		[DllImport("user32.dll")]
		public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
			int Y, int cx, int cy, uint uFlags);
 
		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool SetForegroundWindow(IntPtr hWnd);
	}
}

Now add another file Functions.cs with the following code,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/* DeeShell - A shell replacement for Windows
 * Pravin Paratey (February 19, 2007)
 *
 * Article: http://pravin.insanitybegins.com/articles/deeshell
 *
 * Released under Creative Commons Attribution 2.5 Licence
 * http://creativecommons.org/licenses/by/2.5/
 */
using System;
using System.Windows.Forms;
 
namespace DeeShell
{
	class Functions
	{
		#region Private variables
		private static WinAPI.RECT m_rcOldDesktopRect;
		private static IntPtr m_hTaskBar;
		#endregion
 
		/// <summary>
		/// Resizes the Desktop area to our shells' requirements
		/// </summary>
		public static void MakeNewDesktopArea()
		{
			// Save current Working Area size
			m_rcOldDesktopRect.left = SystemInformation.WorkingArea.Left;
			m_rcOldDesktopRect.top = SystemInformation.WorkingArea.Top;
			m_rcOldDesktopRect.right = SystemInformation.WorkingArea.Right;
			m_rcOldDesktopRect.bottom = SystemInformation.WorkingArea.Bottom;
 
			// Make a new Workspace
			WinAPI.RECT rc;
			rc.left = SystemInformation.VirtualScreen.Left;
			// We reserve the 24 pixels on top for our taskbar
			rc.top = SystemInformation.VirtualScreen.Top + 24;
			rc.right = SystemInformation.VirtualScreen.Right;
			rc.bottom = SystemInformation.VirtualScreen.Bottom;
			WinAPI.SystemParametersInfo((int)WinAPI.SPI.SPI_SETWORKAREA, 0, ref rc, 0);
		}
 
		/// <summary>
		/// Restores the Desktop area
		/// </summary>
		public static void RestoreDesktopArea()
		{
			WinAPI.SystemParametersInfo((int)WinAPI.SPI.SPI_SETWORKAREA, 0, ref m_rcOldDesktopRect, 0);
		}
 
		/// <summary>
		/// Hides the Windows Taskbar
		/// </summary>
		public static void HideTaskBar()
		{
			// Get the Handle to the Windows Taskbar
			m_hTaskBar = WinAPI.FindWindow("Shell_TrayWnd", null);
			// Hide the Taskbar
			if ((int)m_hTaskBar != 0)
			{
				WinAPI.ShowWindow(m_hTaskBar, (int)WinAPI.WindowShowStyle.Hide);
			}
		}
 
		/// <summary>
		/// Show the Windows Taskbar
		/// </summary>
		public static void ShowTaskBar()
		{
			if ((int)m_hTaskBar != 0)
			{
				WinAPI.ShowWindow(m_hTaskBar, (int)WinAPI.WindowShowStyle.Show);
			}
		}
	}
}

Edit Program.cs to look like,

1
2
3
4
5
6
7
8
9
10
static void Main()
{
	// Make new Working Area
	Functions.HideTaskBar();
	Functions.MakeNewDesktopArea();
 
	// Restore Working Area Size
	Functions.RestoreDesktopArea();
	Functions.ShowTaskBar();
}

Put a breakpoint at RestoreDesktopArea(), and Press F5 to compile and run. When you hit the breakpoint, observe that,

  1. The Windows Taskbar has disappeared.
  2. You can try maximizing any open windows. They will now occupy the bottom pixels and leave an empty space of 24 pixels on top.
  3. If you refresh your desktop icons, they will obey these rules too. Doesn’t it make you feel powerful – making the desktop icons do that? Those who answered Yes – Boy! You guys sure need some serious councelling.

Press F5 again to continue and end the program. I know what you’re thinking, “This is not a shell replacement! You’re just hiding the taskbar. Explorer still runs in the background.”

*raises eyebrow* Are you being sassy? Let me tell you a story about what happened to the little boys and girls who were sassy. Santa didn’t leave them any presents that year. So there!

Coming back to the question, take a look at HideTaskBar(). DeeShell can run independently as well as on top of explorer.

Adding a Taskbar

Adding a taskbar will involve,

  1. Creating a new form.
  2. Setting its properties.
  3. Displaying it on startup.

Lets add a new form. Right-click the project in the solution explorer and choose Add -> Windows Form and name it TaskBar. Next, set the following properties:

  • Size: 300, 24
  • Start Position: Manual
  • Control Box: False
  • ShowInTaskbar: False

That completes your basic taskbar. Lets spruce it up a little. First, we’ll add a background image. Double click Resources.resx in the solution explorer. Then click on Add Image Resource and add an image. Set this as the BackgroundImage for your Taskbar window.

Adding Image Resource

Next, we’ll add a TableLayoutPanel. This control will help us organize our taskbar buttons quite nicely. Set the number of rows to 1 and leave the number of columns as two. Set the size of the first column to 60px. Then, set the following properties:

  • Backcolor: Transparent
  • (Name): tableLayoutPanel
  • ColumnCount: 2
  • Dock: Fill
  • Location: 0, 0
  • Margin: 0, 0, 0, 0
  • RowCount: 1

Now, we’ll add a Button called “Exit” which will let us exit DeeShell gracefully. Add a button to column 1 of TableLayoutPanel. Set its Dock property to Fill and set Margin to 0. Add the following code to it’s Click event (I’ve named the function OnExitClick):

private void OnExitClick(object sender, EventArgs e)
{
	Application.Exit();
}

Change Program.cs to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main()
{
	Application.EnableVisualStyles();
 
	// Make new Working Area
	Functions.HideTaskBar();
	Functions.MakeNewDesktopArea();
 
	Application.Run(new Taskbar());
 
	// Restore Working Area Size
	Functions.RestoreDesktopArea();
	Functions.ShowTaskBar();
}

If you run the application now, you’ll see that the height property of Taskbar is not respected. To fix this, add this line to Taskbar.cs:

public Taskbar()
{
	InitializeComponent();
	WinAPI.SetWindowPos(this.Handle, (IntPtr)0, 0, 0, SystemInformation.VirtualScreen.Width, 24, 0x0040);
}

Listing Running Tasks

Add the following to Functions.cs,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// Gets a list of Active Tasks
/// </summary>
public static ArrayList GetActiveTasks()
{
	ArrayList ar = new ArrayList();
	IntPtr child = IntPtr.Zero;
 
	Process[] process = Process.GetProcesses();
	foreach (Process p in process)
	{
		WindowData w;
		if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle.Length > 0)
		{
			w.hwnd = p.MainWindowHandle;
			w.title = p.MainWindowTitle;
			ar.Add(w);
		}
	}
	return ar;
}

You will also have to add these declarations at the beginning of Functions.cs

using System.Diagnostics; using System.Collections;

Now add a ComboBox to our TaskBar form to show the list of running processes. I’ve populated the ComboBox at the start with:

public Taskbar()
{
	InitializeComponent();
	WinAPI.SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, SystemInformation.VirtualScreen.Width, 24, 0x0040);
	ArrayList ar = Functions.GetActiveTasks();
	for (int i = 0; i &lt; ar.Count; i++)
	{
		WindowData w = (WindowData)ar[i];
		cboTaskList.Items.Add(w.title);
	}
}

We’re going to bring the window selected in the ComboBox to the Foreground. To the SelectedIndexChanged event, attach the code:

private void cboTaskList_SelectedIndexChanged(object sender, EventArgs e)
{
	string windowName = cboTaskList.Text;
	IntPtr handle = WinAPI.FindWindow(null, windowName);
	WinAPI.SetForegroundWindow(handle);
}

There you go! Your very own partially skinned shell replacement. It doesn’t do much. It doesn’t mow your lawn or fix your faucet. But it makes one hell of a story – for those times when your kids just refuse to sleep.

I can’t believe the tutorial is done! 5 hours. Whoa!

F.A.Q.

1. How do I prevent the user from closing DeeShell through Alt+F4?

Add this code to the Taskbar’s FormClosing event:

private void Taskbar_FormClosing(object sender, FormClosingEventArgs e)
{
	e.Cancel = true;
}

You’ll also need to add a check to see if the exit was valid (say clicking the exit button) and allow that one to pass.

2. How do I ensure that only one instance of DeeShell runs?

Create a named mutex. Lock it. And let your first line in main() check for the presence of this mutex. If present, DeeShell is already running. Another way would be to use the FindWindow() API.

13 Responses (rss) (trackback)

#1

Anirudh

February 22nd, 2007 at 12:23 am

I would lovvvvvvvvvvvvvveee to make a pretty little shell like konsole or the other linux terminals. I spend most of my time in the commandline, and work on rubyonrails.

Y ou know any place where I’ll get a good functional terminal that’l work on vista?

#2

pravin

February 22nd, 2007 at 10:49 pm

Anirudh: Umm, this is a different sort of shell. It’s a graphical shell.

I’ve been looking for such a shell too. I thought of making one myself, but was too lazy.

It is not very hard to make a konsole like shell for windows. Most of the work is already done for you. All you need to do is patch some things.

I’ll suggest,

- Download the source code for putty. It is in C. You only have to concentrate on the window drawing routines of putty.

- Hook in cmd.exe over it. Or better still, download bash source (win32 version) and hook those two.

And voila! One custom term at your service.

#3

Anirudh

February 22nd, 2007 at 11:06 pm

ohhh. *that* kind of shell. Like talisman/etc. neat stuff. I’ve tried cygwin but couldn’t make much use out of it. I’ll try putty vs cmd.exe and see how it goes.

#4

pravin

February 23rd, 2007 at 6:40 pm

Anirudh: If you’re able to make it, I’d want a copy too :-)

#5

vince

April 4th, 2007 at 3:18 pm

First off thanks for sharing. Any idea how to fix the set desktop area being ignored after recovering from a desktop lock ( Win + L )?

Kind regards,

Vince

#6

Ryan Cromwell

April 12th, 2007 at 10:57 pm

Very cool. We are writing a “blank” shell for our kiosk. This helped since we started in C thinking that would be the best. This jump start will help immensely.

#7

Graham Hickson

April 22nd, 2007 at 5:20 am

This is one great starting point for someone looking into starting on their own shell, but after I just had a play around with it on Vista I can say it needs a few tweaks made to it.

Not sure how the nuts and bolts of the new Vista shell work together but hiding the taskbar does only that, hides the taskbar, but for some reason beyond me, the Vista star menu “orb” stays there waiting for use.

This for my use is great, since I like the new start menu and would happily build up a new shell around it, but for those that would want to completely replace the whole vista shell, it could be troublesome.

All in all, a great piece of writing, hope to see more on the subject in the future.

#8

pravin

April 22nd, 2007 at 4:26 pm

Vince: I’m on a linux system currently and don’t have access to a windows machine. When I do, I’ll look into it. Thanks for reporting!

Ryan: :D Thanks

Graham: Thank you for your kind words. This code is just for illustration and therefore it’s missing a lot of stuff that could handle various scenarios.

If you want to implement a windows shell, hiding taskbar is not the way to go. There’s a registry entry for the Default shell in which you’ll have to replace explorer.exe with your-shell.exe. Search for it on the net. (I can’t, cause I don’t have access to a windows machine right now)

That done, explorer will not be executed on startup and so you will not have the orb or other vista visual elements.

Hope that helps.

#9

Logan

January 2nd, 2008 at 12:18 am

Hello pravin, great article! It came in very handy, though it took me forever to find it.

I do have a question that perhaps you can answer for me without me having to search the whole net all over again: How can a C# application receive an event/notification of when the desktop area(s) change? For example, suppose the user changes the resolutionÂ… I should want the new shell (or in my case an RSS ticker) to accommodate the new screen size.

Similarly, if another program calls SystemParametersInfo with SPI_SETWORKAREA, it would be nice to be notified of the change so the app can adjust itself as necessary.

Is there a quick way to do this?

Thanks again.

#10

Mike

October 15th, 2008 at 4:33 pm

Logan:
This is really an improper way to create what your trying to create as it truly does not adjust the size of the destkop area and it covers icons on most systems. The proper way would be to create an actual Desktop Application Toolbar.
http://www.microsoft.com/msj/archive/s274.aspx

After creating the appbar your application will recieve notifications apon all changes to the desktop and other application toolbars so you can then adjust your appbar accordingly.

#11

joe

December 10th, 2008 at 8:24 am

ummm as far as the last idea, that isnt the greatest. just becuase if this were to be used to create a full shell replacement then there would not be a conventional desktop to place an appbar on

#12

Mike

March 8th, 2009 at 3:54 am

@Joe:
My response was to Logan as he is looking to receive messages about changes to the existing desktop.

And as far as creating a full shell replacement goes, neither this article or using an appbar would work.

If you are adjusting a part of the shell(the desktop) in order to reserve screen space for your app then you are in fact NOT replacing the shell.

#13

RaVe-N

February 4th, 2010 at 5:02 am

Hi! Thanks for a nice article.
I’m also looking to create a shell and was wondering if you know a way to create a Systray Clone without leaving explorer running and peeking into it’s Shell_TrayWnd window? (I really want to replace explorer.exe not use it.)

Thanks!

RaVe-N

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Latest Articles

Feb
19

Join a list of integers in Python

How do you run a string join on a list of integers in Python? After googling for about 10 mins, I gave up and did this. I am sure there is a better way of doing it! [Read More]
Jan
21

Writing a spider in 10 mins using Scrapy

I came across Scrapy a few days back and have grown to really love it. This tutorial will illustrate how you can write a simple spider using Scrapy to scrape data off Paul Smith. All this in 10 minutes. [Read More]

Featured Projects

Document Tagger

Document Tagger

DocTagger lets you automatically classify text documents. Use this as a starting point to write apps that can sort through volumes of unorganized data.

[Read More]

NLP classes for PHP

NLP classes for PHP

This is an ongoing project to develop a set of classes for Natural Language Processing. Some code would be ported from the NLTK project.

[Read More]

This page and its contents are copyright © 2010, Pravin Paratey.