Extended WebBrowser Control Series: WPF WebBrowser and the NewWindow2

To be able to catch popup windows and open them in your own window you
have to manage WebBrowser events like NewWindow2.

But how do you do that in WPF?

Well it isn’t really that difficult. These are the steps that you have to follow:

1. Add a COM reference to a reference to %windir%\system32\shdocvw.dll

2. Add a new CodeFile to your project. Lets say CodeFile1.cs And put this code:

using System;
using System.Runtime.InteropServices;
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IServiceProvider

[return: MarshalAs(UnmanagedType.IUnknown)] 

object QueryService(ref Guid guidService, ref Guid riid);


3. To make an easy example. Lets assume we have a very simple window like:


And in that form we need some code like this:

        private void button1_Click(object sender, RoutedEventArgs e)
            Guid SID_SWebBrowserApp = 
new Guid("0002DF05-0000-0000-C000-000000000046"); IServiceProvider serviceProvider =
(IServiceProvider)myWebBrowser.Document; //<—It seams that you need to
// navigate first to initialize this Guid serviceGuid = SID_SWebBrowserApp; Guid iid = typeof(SHDocVw.IWebBrowser2).GUID; //Here we will get a reference to the IWebBrowser2 interface SHDocVw.IWebBrowser2 myWebBrowser2 =
serviceProvider.QueryService(ref serviceGuid, ref iid);
            //To hook events we just need to do these casts
            SHDocVw.DWebBrowserEvents_Event wbEvents = 
(SHDocVw.DWebBrowserEvents_Event)myWebBrowser2; SHDocVw.DWebBrowserEvents2_Event wbEvents2 =
            //Adding event handlers is now very simple
            wbEvents.NewWindow += 
new SHDocVw.DWebBrowserEvents_NewWindowEventHandler(wbEvents_NewWindow);
            wbEvents2.NewWindow2 += 
new SHDocVw.DWebBrowserEvents2_NewWindow2EventHandler(wbEvents2_NewWindow2); } void wbEvents2_NewWindow2(ref object ppDisp, ref bool Cancel) { //If you want make popup windows to open in your own window
// you need to assign the ppDisp to the .Application of
// the WebBrowser in your window
            Window1 wnd = new Window1();
//Just navigate to make sure .Document is initilialized wnd.myWebBrowser.Navigate(new Uri("about:blank"));
Guid SID_SWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); IServiceProvider serviceProvider = (IServiceProvider)wnd.myWebBrowser.Document; Guid serviceGuid = SID_SWebBrowserApp; Guid iid = typeof(SHDocVw.IWebBrowser2).GUID; SHDocVw.IWebBrowser2 myWebBrowser2 = (SHDocVw.IWebBrowser2)serviceProvider.QueryService(ref serviceGuid, ref iid); ppDisp = myWebBrowser2.Application; } void wbEvents_NewWindow(string URL, int Flags, string TargetFrameName, ref object PostData, string Headers, ref bool Processed) { MessageBox.Show(URL); } private void button2_Click(object sender, RoutedEventArgs e) { myWebBrowser.Navigate(new Uri("file://D:/MyProjects/ExtendedBrowserExample_v2/test0.htm")); }

Now you can manage your popupwindows:


You can download the test application from HERE

Extended WebBrowser Control Series: NewWindow3

Recently an user of the ExtendedBrowser v2 commented that he needed access to the NewWindow3 event.

The NewWindow3 event is raised when a new window is to be created. It extends NewWindow2 with additional information about the new window. 


Private Sub object_NewWindow3( _
	ByRef ppDisp As Object, _
	ByRef Cancel As Boolean, _
	ByVal dwFlags As Long, _
	ByVal bstrUrlContext As String, _
	ByVal bstrUrl As String)


object Object expression that resolves to the objects in the Applies To list. ppDisp Object expression that, optionally, receives a new, hidden WebBrowser or InternetExplorer object with no URL loaded. Cancel A Boolean value that determines whether the current navigation should be canceled. true Cancel the navigation. false Do not cancel the navigation. dwFlags The flags from the NWMFenumeration that pertain to the new window.

typedef enum NWMF 
    NWMF_UNLOADING = 0x00000001,
     NWMF_USERINITED = 0x00000002,
     NWMF_FIRST = 0x00000004,
     NWMF_OVERRIDEKEY = 0x00000008,
     NWMF_SHOWHELP = 0x00000010,
     NWMF_HTMLDIALOG = 0x00000020,
    NWMF_FROMDIALOGCHILD = 0x00000040,
     NWMF_USERREQUESTED = 0x00000080,
     NWMF_USERALLOWED = 0x00000100,
     NWMF_FORCEWINDOW = 0x00010000,
     NWMF_FORCETAB = 0x00020000,
     NWMF_SUGGESTWINDOW = 0x00040000,
     NWMF_SUGGESTTAB = 0x00080000,
     NWMF_INACTIVETAB = 0x00100000

bstrUrlContext The URL of the page that is opening the new window. bstrUrlThe URL that is opened in the new window.

Please notice:

Note   The NewWindow3 event is only fired when a new instance of Internet Explorer is about to be created. Calling showModalDialog or showModelessDialog does not trigger an event because they are not new instances of Internet Explorer. They are implemented as MSHTML host windows, which allows them to render and display HTML content but not hyperlinks between documents.

You can download from here


ExtendedBrowserExampleVBNET.zip (92.56 kb)

Extended WebBrowser Control Series:And the WebBrowser keeps going…

Well recently Kingsley has point me to a lot of useful links to improve the ExtendedWebBrowser. However he found another detail. When in Javascript you do something like a:


Those width and height settings were not being considered in the new window. I researched for I while until I found this great link:

HOW TO: Get Width and Height from window.open() Inside a WebBrowser Host by Using Visual Basic .NET

So basicly I follow the sugested code and added logic in my EventSink class:

        public void WindowSetLeft(int Left)
            ///Should I calculate any diff?
            _Browser.Parent.Left = Left;


        public void WindowSetTop(int Top)
            _Browser.Parent.Top = Top;


        public void WindowSetWidth(int Width)
            int diff = 0;
            diff = _Browser.Parent.Width - _Browser.Width;
            _Browser.Parent.Width = diff + Width;

        public void WindowSetHeight(int Height)
            int diff = 0;
            diff = _Browser.Parent.Height - _Browser.Height;
            _Browser.Parent.Height = diff + Height;

So now when the window opens it takes the specified width, heigth, left and top.

As always


Extended WebBrowser Control Series: WebBrowser Control and window.Close()

I had previously posted an extended version of the WebBrowser Control. This code posted in Opening Popup in a NewWindow and NewWindow2 Events in the C# WebBrowserControl, dealt with some issues when you want to have a form with a WebBrowser and in the enclosed page you have a Javascript code like:

window.open(“ <some url to a page”)

But recently another problem arised. What if you have a Javascript snippet like:


OMG!!! Why haven’t I thought about it. Well Kelder wrote me about this problem and he also sent me some of his\her research results:

Solution (Add WebBrowser as unmanaged code):  blogs.msdn.com/jpsanders/archive/2008/04/23/window-close-freezes-net-2-0-webbrowser-control-in-windows-form-application.aspx

Solution (Add WebBrowser using WM_NOTIFYPARENT override):blogs.msdn.com/jpsanders/archive/2007/05/25/how-to-close-the-form-hosting-the-webbrowser-control-when-scripting-calls-window-close-in-the-net-framework-version-2-0.aspx


Solution (Implementation not detailed): social.msdn.microsoft.com/forums/en-US/winforms/thread/1199c004-9eb2-400d-a118-6e06bca9f1f0/

Proposes changing pop-up links to WebBrowser navigate: dotnetninja.wordpress.com/2008/02/26/prevent-opening-new-window-from-webbrowser-control/Close

problem observed (no solution):www.codeproject.com/KB/cpp/ExtendedWebBrowser.aspx

It seams to me that the better solution is to use jpsanders solution, so I created an ExtendWebBrowser_v2 (the following is the modified fragment):

//Extend the WebBrowser control
public class ExtendedWebBrowser : WebBrowser
    // Define constants from winuser.h
    private const int WM_PARENTNOTIFY = 0x210;
    private const int WM_DESTROY = 2;
    AxHost.ConnectionPointCookie cookie;
    WebBrowserExtendedEvents events;

    protected override void WndProc(ref Message m)
        switch (m.Msg)
            case WM_PARENTNOTIFY:
             if (!DesignMode) 
                if (m.WParam.ToInt32() == WM_DESTROY) 
                    Message newMsg = new Message();
                    newMsg.Msg = WM_DESTROY;
                    // Tell whoever cares we are closing
                    Form parent = this.Parent as Form;
                    if (parent!=null)
            DefWndProc(ref m);
            base.WndProc(ref m);

The problem that might arise with this solution is that the parent might not be a Form but an user control, etc. For a more general aproach I think I should send a WM_DESTROY directly to the parent, but for most cases it works. I’m attaching the code and a sample page called test0.htm. I hope this helps and rembember you can always donate to programming geeks jejejejeje just kidding


Extended WebBrowser Control Series: Opening Popup in a NewWindow

In a previous post, i had published an “Extended Version” of the WebBrowser control
that gave you access to events like the NewWindow2.
This event that is not public in the common WebBrowser control allows you to intercept
the NewWindow event but gives you the posibility to setup the the ppDisp property witch sets
a pointer to the WebBrowser where the new window will be open.

So i setup a small example using this “ExtendedBrowser”.

I created a simple page (well really it was my wife, I know about transport-layer, C++, bits etc, but I never remember HTML syntax):

<H1> This is sample page to test opening a pop up in a new form </H1>
<input type="button" onclick="window.open('test0.htm')"/>

And created a simple form like in the following picture:



Instead of using a WebBrowser control i just used an ExtendedWebBrowser from my previous post.

And added code like:

        private void extendedWebBrowser1_NewWindow2(object sender, NewWindow2EventArgs e)
            //Intercepting this event will allow us to create a new form in which
            //we will open the new webpage, to do that we must set the ppDisp property
            //of the NewWindow2EventArgs
            FormWithExtendedBrowser form1 = new FormWithExtendedBrowser();
            e.PPDisp = form1.extendedWebBrowser1.Application;

When I run the code , it now opens the pop up in my form:


But test if for yourself! :) HERE IS THE CODE

Extended WebBrowser Control Series:NewWindow2 Events in the C# WebBrowserControl

The WebBrowser control for .NET is just a wrapper for the IE ActiveX control. However this wrapper does not expose all the events that the IE ActiveX control exposes.

For example the ActiveX control has a NewWindow2 that you can use to intercept when a new window is gonna be created and you can even use the ppDisp variable to give a pointer to an IE ActiveX instance where you want the new window to be displayed.

So, our solution was to extend the WebBrowser control to make some of those events public.

In general the solution is the following:

  1. Create a new Class for your Event that extend any of the basic EventArgs classes.
  2. Add constructors and property accessor to the class
  3. Look at the IE Activex info and add the  DWebBrowserEvents2 and IWebBrowser2 COM interfaces. We need them to make our hooks.
  4. Create a WebBrowserExtendedEvents extending System.Runtime.InteropServices.StandardOleMarshalObject and DWebBrowserEvents2. We need this class to intercept the ActiveX events. Add methos for all the events that you want to intercept.
  5. Extend the WebBrowser control overriding the CreateSink and DetachSink methods, here is where the WebBrowserExtendedEvents class is used to make the conneciton.
  6. Add EventHandler for all the events.

And thats all.Here is the code. Just add it to a file like ExtendedWebBrowser.cs

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;

    //First define a new EventArgs class to contain the newly exposed data
    public class NewWindow2EventArgs : CancelEventArgs

        object ppDisp;

        public object PPDisp
            get { return ppDisp; }
            set { ppDisp = value; }

        public NewWindow2EventArgs(ref object ppDisp, ref bool cancel)
            : base()
            this.ppDisp = ppDisp;
            this.Cancel = cancel;

    public class DocumentCompleteEventArgs : EventArgs
        private object ppDisp;
        private object url;

        public object PPDisp
            get { return ppDisp; }
            set { ppDisp = value; }

        public object Url
            get { return url; }
            set { url = value; }

       public DocumentCompleteEventArgs(object ppDisp,object url)
           this.ppDisp = ppDisp;
           this.url = url;


    public class CommandStateChangeEventArgs : EventArgs
        private long command;
        private bool enable;
        public CommandStateChangeEventArgs(long command, ref bool enable)
            this.command = command;
            this.enable = enable;

        public long Command
            get { return command; }
            set { command = value; }

        public bool Enable
            get { return enable; }
            set { enable = value; }

    //Extend the WebBrowser control
    public class ExtendedWebBrowser : WebBrowser
        AxHost.ConnectionPointCookie cookie;
        WebBrowserExtendedEvents events;

        //This method will be called to give you a chance to create your own event sink
        protected override void CreateSink()
            //MAKE SURE TO CALL THE BASE or the normal events won't fire
            events = new WebBrowserExtendedEvents(this);
            cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));


        public object Application
                IWebBrowser2 axWebBrowser = this.ActiveXInstance as IWebBrowser2;
                if (axWebBrowser != null)
                    return axWebBrowser.Application;
                    return null;

        protected override void DetachSink()
            if (null != cookie)
                cookie = null;

        //This new event will fire for the NewWindow2
        public event EventHandler<NewWindow2EventArgs> NewWindow2;

        protected void OnNewWindow2(ref object ppDisp, ref bool cancel)
            EventHandler<NewWindow2EventArgs> h = NewWindow2;
            NewWindow2EventArgs args = new NewWindow2EventArgs(ref ppDisp, ref cancel);
            if (null != h)
                h(this, args);
            //Pass the cancellation chosen back out to the events
            //Pass the ppDisp chosen back out to the events
            cancel = args.Cancel;
            ppDisp = args.PPDisp;

        //This new event will fire for the DocumentComplete
        public event EventHandler<DocumentCompleteEventArgs> DocumentComplete;

        protected void OnDocumentComplete(object ppDisp, object url)
            EventHandler<DocumentCompleteEventArgs> h = DocumentComplete;
            DocumentCompleteEventArgs args = new DocumentCompleteEventArgs( ppDisp, url);
            if (null != h)
                h(this, args);
            //Pass the ppDisp chosen back out to the events
            ppDisp = args.PPDisp;
            //I think url is readonly

        //This new event will fire for the DocumentComplete
        public event EventHandler<CommandStateChangeEventArgs> CommandStateChange;

        protected void OnCommandStateChange(long command, ref bool enable)
            EventHandler<CommandStateChangeEventArgs> h = CommandStateChange;
            CommandStateChangeEventArgs args = new CommandStateChangeEventArgs(command, ref enable);
            if (null != h)
                h(this, args);

        //This class will capture events from the WebBrowser
        public class WebBrowserExtendedEvents : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2
            ExtendedWebBrowser _Browser;
            public WebBrowserExtendedEvents(ExtendedWebBrowser browser)
            { _Browser = browser; }

            //Implement whichever events you wish
            public void NewWindow2(ref object pDisp, ref bool cancel)
                _Browser.OnNewWindow2(ref pDisp, ref cancel);

            //Implement whichever events you wish
            public void DocumentComplete(object pDisp,ref object url)
                _Browser.OnDocumentComplete( pDisp, url);

            //Implement whichever events you wish
            public void CommandStateChange(long command, bool enable)
                _Browser.OnCommandStateChange( command,  ref enable);

        [ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), TypeLibType(TypeLibTypeFlags.FHidden)]
        public interface DWebBrowserEvents2
            void CommandStateChange([In] long command, [In] bool enable);
            void DocumentComplete([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, [In] ref object URL);
            void NewWindow2([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object pDisp, [In, Out] ref bool cancel);

        [ComImport, Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), TypeLibType(TypeLibTypeFlags.FOleAutomation | TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)]
        public interface IWebBrowser2
            void GoBack();
            void GoForward();
            void GoHome();
            void GoSearch();
            void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            void Refresh();
            void Refresh2([In] ref object level);
            void Stop();
            object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            bool TopLevelContainer { get; }
            string Type { get; }
            int Left { get; set; }
            int Top { get; set; }
            int Width { get; set; }
            int Height { get; set; }
            string LocationName { get; }
            string LocationURL { get; }
            bool Busy { get; }
            void Quit();
            void ClientToWindow(out int pcx, out int pcy);
            void PutProperty([In] string property, [In] object vtValue);
            object GetProperty([In] string property);
            string Name { get; }
            int HWND { get; }
            string FullName { get; }
            string Path { get; }
            bool Visible { get; set; }
            bool StatusBar { get; set; }
            string StatusText { get; set; }
            int ToolBar { get; set; }
            bool MenuBar { get; set; }
            bool FullScreen { get; set; }
            void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize);
            WebBrowserReadyState ReadyState { get; }
            bool Offline { get; set; }
            bool Silent { get; set; }
            bool RegisterAsBrowser { get; set; }
            bool RegisterAsDropTarget { get; set; }
            bool TheaterMode { get; set; }
            bool AddressBar { get; set; }
            bool Resizable { get; set; }