This program allows to host several open Windows in one parent window so that you can easily access and navigate between them, as well as clean up space in the taskbar. The idea of creating this program came to me when I was reading an article by Jay Nelson: Hosting EXE Applications in a WinForm project. Instead of hosting just a single executable inside a WinForm project, I decided to have a tabcontrol and host different Windows on different tabs. This will allow to group similar Windows together, easily navigate between them and clean up space in the taskbar. Interested? In that case, let's start exploring the application.

All the functionality of this application is achieved using Windows API functions, so you should be familiar with basic winapi programming. Consequently you should know what P/Invoke is and how it works. If you are familiar with the article: Window Tray Minimizer, then it will help you a lot.

When you run the application, it hides the main form and an icon is shown in the system tray. If you right-click the icon, a context menu will be shown which has two main buttons: 'Tab Windows' and 'Tab all Windows'. When you click 'Tab Windows', a new window will pop up with a list of open Windows. The list can be filtered by the title of the Windows. When you select the Windows you wish to tabify and click 'OK', a new window will be created and all the checked Windows will be hosted in a tabcontrol. There will be one more tabpage called Menu which has two buttons: one for adding open Windows to the tabcontrol and another for choosing files that will be automatically opened in new tabs. You can also drag & drop files or folders from Windows Explorer on this tabpage for having them opened in new tabs automatically. You can navigate between tabs by clicking Ctrl+1, Ctrl+2 and so on or you can just simply hover mouse over the tab and it will be selected automatically. When selected tab changes, icon of the host window is changed either with the icon of the executable file that created current window or with the icon of the window itself. Minimizing the host window minimizes it to tray, but if it is not what you want, you can turn off the feature from the options dialog.

Before we begin exploring the application itself, I'd like to introduce a class for storing simple properties of a window and methods for manipulation on it. The main properties are: Handle, Location, Parent, Size and Title. The main methods include closing the window, getting the path of the executable file that created the window, moving it, setting/restoring parent and setting style. There is also one static method that enumerates all open Windows. The class has one constructor that takes the window's handle as a parameter and sets its simple properties. Here is a class diagram:

When you click the 'Tab Windows' button, a window is shown which lists all open Windows. In order to enumerate all Windows, you should call the winapi function called EnumWindows. The function takes two parameters. The first one is a pointer to a callback function. The code snippet below shows how this function works:

After this, we need to filter this list. Firstly, we need to get rid of the Windows that were opened by our application so that we don't get host Windows hosting other host Windows. This can be done for each returned window by finding the path of the executable that created the window and comparing it to our application's location. Secondly, if the user has selected to ignore Windows without a title, we have to remove them.

When a user selects those Windows that are to be tabbed and clicks OK, a new 'host' window is created and selected Windows are passed to it. When the host is displayed, it adds a new tabpage for each window and displays the window.

Detecting drag 'n' drop of files from Windows Explorer on the menu tab is detected by the component that comes with the source code of this book: Windows Forms 2.0 Programming. When files or folders are dropped on the form or user selects them by clicking 'Open files in new tab', they are filtered and a new process is started using the filename.

The ProcessFile method starts a new process based on a parameter, waits 5 seconds for an application to become idle and then checks its MainWindowHandle property. If a folder was dropped, then it tries to get the handle to the window which was created by using winapi FindWindow function.

Except just clicking the tab with mouse which you wish to select there are two ways to navigate between them: You can either click Ctrl+1, Ctrl+2, etc. at the same time to switch to the corresponding tab or you can simply hover mouse over the tab and it will be selected automatically. The code snippets below show how these are accomplished.

Code Snippet for Ctrl+1, Ctrl+2, etc.

privatevoid tabs_KeyDown(object sender, KeyEventArgs e)
{
//Check if Ctrl key is pressed and that there is
//corresponding tab item to the number which was pressed.
if (e.Control && e.KeyValue>48 && e.KeyValue<58 && tabs.Items.Count>=(e.KeyValue-48))
{
tabs.SelectedItem = tabs.Items[e.KeyValue - 49];
}br>}

Code Snippet for Automatically Selecting Tab when Mouse is Moved Over It

When you change active tab in host window, the host window icon changes either with the icon of the executable file that created the currently displayed window or with the icon of the window itself that is displayed. So we need to retrieve either the executable icon or the window's icon.

In order to retrieve the executable icon we need to call SHGetFileInfo() and pass the path of the executable file and an instance of SHFILEINFO structure. After you have retrieved a handle to the icon, you must call DestoyIcon() API to prevent a memory leak.

You can add the program to start-up from the options Window. To add program to start-up, you need to navigate to HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run key, create a new string and set its value equal to the application's path. Removing the program from start-up is easier: you just remove the value. The code snippet below shows how to do it:

If you try to launch the second instance of the application, you will get a message box saying that it is already running. This is achieved using the mutex class. Mutex allows to share resources between threads. When the first instance of the program is launched, it creates a new mutex. When a second instance is launched, it checks the existence of the mutex. If it exists, then it exits. When the first instance quits, it releases the existing mutex.

Feature #1: When you change the active tab in the host window, the host window icon changes either with the icon of the executable file that created the currently displayed window or with the icon of the window itself. This behaviour can be configured from the options dialog.

About the Author

Comments and Discussions

Let me say thank you first! A great library.
1 question:
when I am hosting a windows file explorer in the tab, the scroll bar of the file explorer is missing.
But I released that tab, it showed up.
Why?

when open a java application process in this app tab , the focus is lost and the cursor not appear in the text area of this app. (the java app work like a console with a main input area to input command)

If you consider updating this, try it out with Vista's Windows Explorer. The non-standard frame is visible through the tabs (it works, just looks a little strange)

-- update --
After using this for sometime, it really rocks! You know how hard Vista's explorer is to use? Well this makes it bearable by allowing several instances to be tabbed (like QTtabBar, but much lighter and no shell extension needed)

I have found a bug that I can't seem to resolve that occurs when one of the tabbed processes hangs/sleeps, then the other tabs become inaccessible when clicking on them. Hover (mouse-over) still works to change tabs, but if you disable the hover option, clicking on the other tabs does not fire the change tab event until the process that is hung/sleeping finishes processing.

Problem: When one tabbed processes hangs/sleeps, the mouse clicking on another tab will hang the whole tabifier window until the hang/sleep is finished.

Steps to recreate:
1. Create a simple EXE/GUI with a button that calls System.Threading.Thread.Sleep(10000);
2. Start two instances of this EXE
3. Start Window Tabifier
4. Disable mouse-over tab switching in Window Tabifier
5. Add the two instances of the sleeper EXE as tabs
6. Click the button in one of the instances that sleeps for 10 seconds
7. IMMEDIATELY try to switch to a different tab: This does not switch until the 10 seconds are up.

Why this is a problem: A process that freezes will not allow me to access any other tabbed processes, and any work I am doing in them may not be saved, etc, etc...

Notes:
-This problem only occurs on some systems. I use Windows XP SP3, and have run my release & debug mode EXEs and this problem occurs on only some machines. On others, it switches tabs fine (my dev machine with VS 2008, this works fine.
-I am using VS 2008, have tried .NET 2.0 and 3.5, in release a debug modes.

This Article is very nice and helpfull. Can you pls. tell me from where i'll get
that Tabstrip.dll's code and DragAndDropFileComponent.dll's source code... I would
like to see the full code... because am not much expert in .NET.
Thanks in Advance.

I just felt the need to publicly aknowledge how much help this app has been for me. I am currently working in an environment that uses windows XP and office 2003. I am used to office 2007 in vista, so "minimize outlook to tray" is a standby to me. Thanks to this, i get the same functionality.

Additionally, I am able to run multiple IE windows without cluttering up my workspace, and was just able to integrate the "window grab" into a testing app for Watir.

When I was searching for projects like yours my intention was to organize my Windows Explorer windows.
Therefore your Possible Enhancement of detecting the WM_SETTEXT message is vital.
When the the selected directory in a Explorer window changes and the caption will not be updated, how to navigate?

Your first enhancement of detetcting opening windows would be nice, as well. The idea could be to centralize all windows of certain applications in a Tabifier host window.
E.g. all Explorer windows are combined in one Tabifier instance and all console windows are combined in another. This should be customizable.

Some goodies would be bookmarks and something like projects, which make it possible to group windows to reopenable projects.

I think your project has the potential for a real good and useful app. If continued.

Thanks. It really makes me feel better when I know that someone liked what I did Thanks for the interesting comments too.

Marcus Deecke wrote:

Therefore your Possible Enhancement of detecting the WM_SETTEXT message is vital.
When the the selected directory in a Explorer window changes and the caption will not be updated, how to navigate?

Your first enhancement of detecting opening windows would be nice, as well.

Unfortunately it's quite difficult if not impossible to use global hooks from c#

Marcus Deecke wrote:

The idea could be to centralize all windows of certain applications in a Tabifier host window.

It's an interesting idea. However, people usually don't have many instances of same application open so the application might lost its idea if the taskbar is still full of windows. But having an option is possible.

I think it would be better to organize windows from same application in tabs next to each other. E.g. first all tabs from windows of explorer, than all tabs from firefox and so on. This should be customizable as well.

The best option would be to have the ability to reorder tabs at runtime. Ideally, I am looking for a tabcontrol that supports runtime reordering of tabs, icons for each tab (both exist in firefox) and close button (like in this application). One possible way of tab reordering is to show a window that lists all open tabs and allows user to reorder them there and will automatically apply it to existing tabs.

Actually (IIRC - it's been a while) using global hooks from C# is a matter of creating a small, simple C dll that does the hooking and then loading that via Win32/PInvoke as an initialization step of the C# exe. My C# WinForms exe was able to successfully utilize realtime hook event data for some of the WH_SHELL hooks as well as WH_KEYBOARD and WH_MOUSE.

It's been maybe 3-4-5-6 years since I worked on the project, but the solution ended up being rather elegant in that the C files (one .C and one .H) are very small and the communication between the C dll and the C# exe is dead-simple.

Of course, this is all from memory, and memory is not always a 1:1 mapping to the reality of the past In any event, my interest is piqued now, so I'll likely dig up the old project when I get home. Let me know if you're interested and I'll make the effort for you, because the work you've done on this project is superb and I'd love to get you over a technical hump, if possible.

One thing that I didn't have to contend with back then was 64-bit Windows, which leads to modifying the code to handle compiling to either target (which I have but a bare understanding of), and then the ramifications that brings to the .NET side of the equation. Sounds like some gritty, hardcore programming fun

As for the behavior on vista, yes I am aware about it. The application was initially developed for xp so in xp there was only one window to exclude I am thinking about adding some sort of rules to the application. The rules will allow users to exclude certain windows based on title of the window or path of the executable path. I just need some time to think about it and decide how to design it.

Few bug-reports: When you host an application and start working on it, the focus from the host application, i.e. Main form is lost. Thus, you cannot use Ctrl-Tab and other shortcuts for the host. Any ideas, how to take care of this? Also, if you have focus on a hosted application, say IE, and you open a new window (untabbed), say Notepad, the Notepad is on top (which is expected). But if you click on the IE window (tabbed inside our application), Notepad does not go to background and remains on top, though the focus from the Notepad is gone.

If my problems are not clear, do let me know and I will try to explain them further.

It looks as if it could potentially be very helpful to anybody making a "commander" style application or tabbed explorer window style program; instead of attempting to slavishly replicate all the bells and whistles of Explorer using tons of COM interop, PIDLs, treeviews, listviews, etc (like some projects hosted on CP), just start a real window of whatever version of Explorer is present on the system. Instead of trying to create a file viewer, just start whatever is available or allow the end-user to specify their own (e.g., Irfanview for pictures, EditPlus for textfiles, etc).

Of course, as defaults, built-in ones doing it the hard way could be used, I suppose.

And vice versa.
If Tabifier is not maximized, a window is added, then Tabifier is maximized, the added window is not maximized.
That looks a bit strange. Never mind, if you know about this behaviour, just keep Tabifier maximized.

Ok. I need your expert help. I am trying this in VB. Just trying to attach a window to a panel. I have been trying this for sometime with no luck. I am getting the window handle for the client. Setting the parent to be the panel's window handle. Then, I set the style and move the window to the panel's location and size. But i have been unable to get the window attached to the panel. Everything works fine but I just cannot get the window attached to the panel. What makes the window attach to another window? How do you do it? Just can't seem to figure that out.