Curiosity killed the Programmer
Inspired by "Google Chrome" web-browser I tried to create some small application and called it "Casper Browser". I tried to check the approach of using multi-process architecture for web-browser and for .Net applications in general. At the past I built a couple multi-process applications in .Net, but these applications were GUI-less and shared functionality by using ".Net Remoting" mechanism.
The main problem while building this small test-project was to get "Main-Form" from another process and to put it in main application's GUI (as new tab in tabs' container). I knew that "Process" class contains pointer (IntPtr -> MainWindowHandle) to Main-Form of the process, if that process contains main form that used by "Application.Run(...)" procedure (this is very common in Win-Forms applications in .Net).
When I tried to create "Control" or "Form" by using function "FromHandle" with "MainWindowHandle", it always returned NULL:
| using (var p1 = Process.Start(DEF_EXE_PROC_PATH)){if (p1 == null) return;
Thread.Sleep(1000); // wait for main frm. init. var h1 = p1.MainWindowHandle;var c1 = FromHandle(p1.MainWindowHandle); // returns NULL
} |
The googling for "Process + MainWindowHandle + FromHandle + C#" returned bunch of useless links, except one with project "Window Tabifier". This project uses API of "kernel32.dll", "user32.dll" and other DLLs of windows. Personally, I never liked using of "[DllImport(...)]", it's like mixing "unclear" (unsafe) code with "clean" .Net code (my opinion). Anyway, Giorgi Dalakishvili did a good job and thanks to his example I get a "temporary" solution for my problem with NULL ("temporary" -> before I'll find more "clean" solution without using of "DllImport").
The new code:
| using(var p1 = Process.Start(DEF_EXE_PROC_PATH)){if (p1 == null) return;
//p1.WaitForInputIdle(); // useless for this proc. Thread.Sleep(1000); // wait for main frm. init.var h1 = p1.MainWindowHandle; // create new tabvar tab1 = new TabPage(p1.MainWindowTitle);tabsContainer.TabPages.Add(tab1);tabsContainer.SelectedTab = tab1; // create win. ref. and put it new tabvar w = new window(h1);w.SetParent(tab1.Handle);w.Move(Point.Empty, tab1.Size, true);windows.Add(w);
} |
This example uses two classes that I copied from "Window Tabifier" project. The class "winapi" contains different API function that extracted from DLLs, these functions are used by "window" class (wrapper for window handler).
Back to main goal of the application:
"Google Chrome" uses the approach of "each browser-tab runs in separate process" and all tabs are added in one tabbified browser window.
As you can see, each tab is presented as process in "Task Manager", has own environment and uses separated resources (see CPU and Memory parameters).
The web-sites for tests: CNN, NY-Times and IHT.
So I wanted to simulate this approach and built small application that uses "WebBrowser" class that included in .Net SDK.
My simple "Casper Browser" with 3 tabs with same web-sites:
As you can see, each tab is presented as separated process, each process uses much more memory then "Chrome" processes, that because I'm using MS "WebBrowser" that is much "heavier" than Chrome's "WebKit" (render engine).
But, the simple approach of using multi-process application for web-browser is working and seems efficient, even with MS "WebBrowser".
Each process uses about 25~30 MB, total: ~88 MB.
Killing one process (browser-tab):
Killing of process with "WebBrowser" will close form and tab that contained this form will be empty. This means that closing of integrated process didn't cause collapsing of main application. If some "BrowserTab" (as integrated process) will collapse because of fatal error/exception that wasn't treated/caught in it's code, main application will continue running. Same approach is good in case that some tab will stack, but this will not affect main application or other tabs.
Now I'll try the "old approach", each tab will contain "WebBrowser" form as a part of main application.
// create new tab var tab1 = new TabPage(); tabsContainer.TabPages.Add(tab1); tabsContainer.SelectedTab = tab1; // create browser form var frm = new BrowserForm{TopLevel = false,Parent = tab1,Dock = DockStyle.Fill,Visible = true};
forms.Add(frm); tab1.DataBindings.Add("Text", frm, "Text"); |
Now we have one process that contains tabs with browsers. If any browser will stack, the entire application will stack, if any browser will collapse the entire application will collapse too.
In case of memory usage single process uses about ~61 MB that is 27 MB less then multi-process version, but it has the all mentioned disadvantages.
If I'll resolve the problem of functionality sharing and other stuff in multi-process version, I'll be able to create some general model for multi-process application with on GUI.
That all for this time, the project sources you can download from here. I'm planning to learn "WebKit" SDK and how to implement it for .Net, so next time I'll try to build browser that will be base on it's render engine, that much lighter than MS Browser object.
PS
Of course I'm not responsible for any damage that may be caused by using this post or by using my code sources.