Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Tabbed Multi Process Application

4.90/5 (18 votes)
8 Apr 2014CPOL4 min read 32.8K  
Multi-process application - Create your own tabbed application container.

Introduction

Sometimes you might have wondered how Chrome browser creates different tabs as different process and how they add into a tab. This article is all about creating a Tabbed multi process application.
Multi process architecture is a known concept to all and has been in use for the past several years, for example, Google chrome, a popular browser where each tab is a separate process. How do they do it ?
Few months ago, a similar requirement was raised in our team too, which prompted me to use such a design. This application helps client in easy management of different networks and also easy identification of user interface as the application is tabbed.

This article is all about how each tab could host different application processes.

Background

Starting from the very basics, in Windows, each UI object has a unique value called handle (refer to the below link). A handle is a pointer and in C#, for every UI object, there is property named Handle using which we could retrieve its Handle. Using this handle and using Windows API, we can easily embed an application into a Windows UI component.

Now let us make our hands wet....

Start Visual Studio and create a solution with two Windows applications in it, one being Child and another being Parent - both should be in the same platform (You could use any existing application as a child application like WordPad, I would suggest to target x86 platform initially if using a legacy application like Notepad).

In the parent application, add a ‘Table layout panel’ control and configure it with two columns.

In the left column – you could add a treeview / buttons as per your choice for launching the application.

In the right column, add a tab panel and set the Dock Property to Fill.

In the parent application, add static classes named WindowsAPI and WindowsConstants. (These classes will help in calling Windows API.)

In the WindowsAPI class, add namespace reference to “Microsoft.Win32” and “System.Runtime.InteropServices Now, modify the class definition with the following code:

C#
[DllImport("user32.dll",SetLastError = true)]  
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 
[DllImport("user32.dll", CharSet =CharSet.Auto)] 
[return: MarshalAs(UnmanagedType.Bool)]<span></span>
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

SetParent API - Use this to create forms and then place child windows on it.

ShowWindowAPI - Sets the specified window's show state.

In the class WindowsConstants, add the following constants:

C#
public static readonly int SW_MAXIMIZE = 3;  

Coming back to the Mainform of the parent application, subscribe the click event of the button/treeview; add the following code in the click event handler.

C#
Process process =Process.Start("Wordpad.exe"); 
process.WaitForInputIdle();
tab.Text = "Wordpad "+ counter;
WindowsAPI.SetParent(process.MainWindowHandle,tab.Handle); 
WindowsAPI.ShowWindow(process.MainWindowHandle,WindowsConstant.SW_MAXIMIZE);

This will make the child application (say notepad.exe here) to attach a process to a tab. But the new process contains title bar menubar and border, which makes it easy to close the application. When we are in control of both child and parent application, we can easily get over this problem by changing the Form properties (BorderStyle). But how can we control the menu and border properties of an application which is out of our control (Say the case of wordpad.exe)? Like SetParent, there are Windows API for changing the Windows border of any application.

Modify the WindowsAPI class to accommodate new WindowsAPI.

C#
public static IntPtr GetWindowLongPtr(HandleRef hWnd, int nIndex)
{
  if (IntPtr.Size == 8)// This is towork in both 64 bit and 32 bit 
      return GetWindowLongPtr64(hWnd,nIndex); 
  else  
      return GetWindowLong32(hWnd, nIndex); 
}  
[DllImport("user32.dll", EntryPoint= "GetWindowLong")]
private static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex);
 
[DllImport("user32.dll", EntryPoint= "GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex); 
public static IntPtrSetWindowLongPtr(HandleRef hWnd, int nIndex, IntPtr dwNewLong)
{  
   if (IntPtr.Size == 8)// This is to work in both 64 bit and 32 bit
      return SetWindowLongPtr64(hWnd,nIndex, dwNewLong);
  else                
      return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
}
[DllImport("user32.dll",EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(HandleRef hWnd, int nIndex, int dwNewLong); 
[DllImport("user32.dll",EntryPoint = "SetWindowLongPtr")]
  
 
private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);

Now modify the WindowsConstants class with the following values:

C#
public static readonly int GWL_STYLE = -16;
public static readonly UInt32 WS_CAPTION = 0xC00000; 
public static readonly UInt32 WS_BORDER = 0x00800000; //window with border 
public static readonly UInt32 WS_DLGFRAME = 0x00400000;

Now in the click event handler of the button, modify the code to remove the title bar and form border.

C#
long style =WindowsAPI.GetWindowLongPtr(new HandleRef(this, process.MainWindowHandle), 
WindowsConstants.GWL_STYLE).ToInt64();
style= style & ~( WindowsConstants.WS_CAPTION |WindowsConstants.WS_BORDER | WindowsConstants.WS_DLGFRAME);  
IntPtr styleValue = new IntPtr(style); WindowsAPI.SetWindowLongPtr(new HandleRef
( this, process. MainWindowHandle), WindowsConstants.GWL_STYLE, styleValue);

This will initially get the window style of the child application’s main window and then remove the caption, border and dialog frame properties then reset the updated style to the mainwindow.

Now this will really help in removing the border and title bar from a window.

But still, we haven’t made the new application the child of the parent application.

We need to do this because, on termination of the parent application, its child application may not all the time get removed from the process queue. This will result in execution of the application in background without our knowledge consuming the processor resources and memory which is a waste.

Now modify the WindowsConstants class by adding new constants.

C#
public static readonly long WS_CHILD =0x40000000L;
public static readonly long WS_POPUP =0x80000000L;

In the button click event handler, once again set the window style.

C#
 style = WindowsAPI.GetWindowLongPtr(new HandleRef(this, tab.Handle), WindowsConstants.GWL_STYLE).ToInt64();
style &= ~ WindowsConstants.WS_POPUP;style|= WindowsConstants.WS_CHILD;
 styleValue = new IntPtr(style); //Setting window to be child of current application and the popup behaviour of window is removed. 
WindowsAPI.SetWindowLongPtr(new HandleRef(this, tab.Handle), WindowsConstants.GWL_STYLE, styleValue);   

This will help in removing the child application on closing the parent from process queue.

Finally, the button's click event handler code is as follows:

C#
private void StartWordPadButton_Click(object sender, EventArgs e)
{
Process ProcessInfo = Process.Start("Wordpad.exe"); // You may give your
 //child application path here
ProcessInfo.WaitForInputIdle();
TabPage tab = new TabPage();
tab.Text = "Wordpad ";
tabControl1.TabPages.Add(tab);
long style =0;
style = WindowsAPI.GetWindowLongPtr(new HandleRef
(this, ProcessInfo.MainWindowHandle), WindowsConstant.GWL_STYLE).ToInt64();
style = style & ~(WindowsConstant.WS_CAPTION |
WindowsConstant.WS_BORDER |WindowsConstant.WS_DLGFRAME);

IntPtr styleValue = new IntPtr(style);
WindowsAPI.SetWindowLongPtr(new HandleRef(this, 
ProcessInfo.MainWindowHandle), WindowsConstant.GWL_STYLE, styleValue);
style = WindowsAPI.GetWindowLongPtr(new HandleRef
(this, ProcessInfo.MainWindowHandle), WindowsConstant.GWL_STYLE).ToInt64();  
style &= ~WindowsConstant.WS_POPUP;

style |= WindowsConstant.WS_CHILD;
styleValue = new IntPtr(style);
//Setting window to be child of current application and the popup behaviour of
 window is removed. 

WindowsAPI.SetWindowLongPtr(new HandleRef(this, 
ProcessInfo.MainWindowHandle), WindowsConstant.GWL_STYLE, styleValue);
WindowsAPI.SetParent(ProcessInfo.MainWindowHandle,tab.Handle);
WindowsAPI.ShowWindow(ProcessInfo.MainWindowHandle,WindowsConstant.SW_MAXIMIZE)
}

Your suggestions and feedback will be very helpful to me for my upcoming articles.

License

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