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):

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

And created a simple form like in the following picture:

image

 

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();
            form1.Show();
            e.PPDisp = form1.extendedWebBrowser1.Application;
        }

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

image

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

Migration of ActiveX UserDocuments to C# or .NET

This post describes an an interesting workaround that you can use to support the migration of ActiveX Documents with the Artinsoft Visual Basic Upgrade Companion which is one of the Artinsoft \ Mobilize.NET tools you can use to modernize your Visual Basic, Windows Forms and PowerBuilder applications.

Currently the Visual Basic Upgrade Companion does not allow you to process ActiveX Document directly, but there is a workaround: in general ActiveX Document are something really close to an User Control which is a element that is migrated automatically by the Visual Basic Upgrade Companion.

This post provides a link to a tool (DOWNLOAD TOOL) that can fix your VB6 projects, so the Visual Basic Upgrade Companion processes them. To run the tool:

1) Open the command prompt

2) Go to the Folder where the .vbp file is located

3) Execute a command line command like:

 FixUserDocuments Project1.vbp

This will generate a new project called Project1_modified.vbp. Migrate this new project and now UserDocuments will be supported.

  

First Some History
 

VB6 allows you to create UserDocuments, which can be embedded inside an ActiveX container. The most common one is Internet Explorer. After compilation, the document is contained in a Visual Basic Document file (.VBD) and the server is contained in either an .EXE or .DLL file. During development, the project is in a .DOB file, which is a plain text file containing the definitions of the project’s controls, source code, and so on.

If an ActiveX document project contains graphical elements that cannot be stored in text format, they will be kept in a .DOX file. The .DOB and .DOX files in an ActiveX document project are parallel to the .FRM and .FRX files of a regular Visual Basic executable project. 

The trick to support ActiveX documents is that in general they are very similar to UserControls, and .NET UserControls can also be hosted in a WebBrowser. The following command line tool can be used to update your VB6 projects. It will generate a new solution where UserDocuments will be defined as UserControls.

 

If you have an ActiveX document like the following: 

 

Then after running the tool you will have an Project like the following:

 

So after you have upgraded the projet with the Fixing tool, open the Visual Basic Upgrade Companion  and migrate your project.

After migration you will get something like this:

 

 

To use your migrated code embedded in a Web Browser copy the generated assemblies and .pdb to the directory you will publish:

Next create an .HTM page. For example UserDocument1.htm

The contents of that page should be something like the following:

 

<html>

<body>

<p>ActiveX Demo<br> <br></body>

<object id="UserDocument1"

classid="http:<AssemblyFileName>#<QualifiedName of Object>"

height="500" width="500" VIEWASTEXT>   

</object>

<br><br>

</html>

 

For example:

<html>

<body>

<p>ActiveX Demo<br> <br></body>

<object id="UserDocument1"

classid="http:Project1.dll#Project1.UserDocument1"

height="500" width="500" VIEWASTEXT>   

</object>

<br><br>

</html>

  

 Now all that is left is to publish the output directory.
To publish your WinForms user control follow these steps.

  1. Create a Virtual Directory:

  1. A Wizard to create a Virtual Directory will appear.

 

 Click Next

 Name the directory as you want. For example Project1. Click Next

 Select the location of your files. Click the Browse button to open a dialog box where you can select your files location. Click Next

 Check the read and run scripts checks and click next

 Now Click Finish

  1. Properties for the Virtual Directory will look like this:

 

NOTE: to see this dialog right click over the virtual directory

 

  1. Now just browse to the address lets say http:\\localhost\Project1\UserDocument1.htm

 And that should be all! :)

 

 

 

The colors are different because of the Host configuration however a simple CSS like:

 

<style>

 body {background-color: gray;}

</style>

 

Can make the desired change:

 

 

 

Notice that there will be security limitations, for example for thinks like MessageBoxes.

You can allow restricted operations by setting your site as a restricted site:

 

For example:

 

 

Restrictions

The constraints for this solution include:

 

* This solutions requires Windows operating system on the client side

* Internet Explorer 6.0 is the only browser that provides support for this type of hosting

* It requires .NET runtime to be installed on the client machine.

* It also requires Windows 2000 and IIS 5.0 or above on the server side

 

Due to all of the above constraints, it might be beneficial to detect the capabilities of the client machine and then deliver content that is appropriate to them. For example, since forms controls hosted in IE require the presence of the .NET runtime on the client machine, we can write code to check if the client machine has the .NET runtime installed. You can do this by checking the value of the Request.Browser.ClrVersion property. If the client machine has .NET installed, this property will return the version number; otherwise it will return 0.0.

 

Adding a script like:

 

<script>

 if ((navigator.userAgent.indexOf(".NET CLR")>-1))

 {

      //alert ("CLR available " +navigator.userAgent);

 }

  else

      alert(".NET SDK/Runtime is not available for us from within " +            "your web browser or your web browser is not supported." +            " Please check with http://msdn.microsoft.com/net/ for " +            "appropriate .NET runtime for your machine.");

 

</script>

Will help with that.

 References:

 

ActiveX Documents Definitions:

http://www.aivosto.com/visdev/vdmbvis58.html

 

 

Hosting .NET Controls in IE

http://www.15seconds.com/issue/030610.htm

 

 

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
            base.CreateSink();
            events = new WebBrowserExtendedEvents(this);
            cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));

        }

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

        protected override void DetachSink()
        {
            if (null != cookie)
            {
                cookie.Disconnect();
                cookie = null;
            }
            base.DetachSink();
        }

        //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
        {
            [DispId(0x69)]
            void CommandStateChange([In] long command, [In] bool enable);
            [DispId(0x103)]
            void DocumentComplete([In, MarshalAs(UnmanagedType.IDispatch)] object pDisp, [In] ref object URL);
            [DispId(0xfb)]
            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
        {
            [DispId(100)]
            void GoBack();
            [DispId(0x65)]
            void GoForward();
            [DispId(0x66)]
            void GoHome();
            [DispId(0x67)]
            void GoSearch();
            [DispId(0x68)]
            void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            [DispId(-550)]
            void Refresh();
            [DispId(0x69)]
            void Refresh2([In] ref object level);
            [DispId(0x6a)]
            void Stop();
            [DispId(200)]
            object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xc9)]
            object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xca)]
            object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xcb)]
            object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
            [DispId(0xcc)]
            bool TopLevelContainer { get; }
            [DispId(0xcd)]
            string Type { get; }
            [DispId(0xce)]
            int Left { get; set; }
            [DispId(0xcf)]
            int Top { get; set; }
            [DispId(0xd0)]
            int Width { get; set; }
            [DispId(0xd1)]
            int Height { get; set; }
            [DispId(210)]
            string LocationName { get; }
            [DispId(0xd3)]
            string LocationURL { get; }
            [DispId(0xd4)]
            bool Busy { get; }
            [DispId(300)]
            void Quit();
            [DispId(0x12d)]
            void ClientToWindow(out int pcx, out int pcy);
            [DispId(0x12e)]
            void PutProperty([In] string property, [In] object vtValue);
            [DispId(0x12f)]
            object GetProperty([In] string property);
            [DispId(0)]
            string Name { get; }
            [DispId(-515)]
            int HWND { get; }
            [DispId(400)]
            string FullName { get; }
            [DispId(0x191)]
            string Path { get; }
            [DispId(0x192)]
            bool Visible { get; set; }
            [DispId(0x193)]
            bool StatusBar { get; set; }
            [DispId(0x194)]
            string StatusText { get; set; }
            [DispId(0x195)]
            int ToolBar { get; set; }
            [DispId(0x196)]
            bool MenuBar { get; set; }
            [DispId(0x197)]
            bool FullScreen { get; set; }
            [DispId(500)]
            void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers);
            [DispId(0x1f7)]
            void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize);
            [DispId(-525)]
            WebBrowserReadyState ReadyState { get; }
            [DispId(550)]
            bool Offline { get; set; }
            [DispId(0x227)]
            bool Silent { get; set; }
            [DispId(0x228)]
            bool RegisterAsBrowser { get; set; }
            [DispId(0x229)]
            bool RegisterAsDropTarget { get; set; }
            [DispId(0x22a)]
            bool TheaterMode { get; set; }
            [DispId(0x22b)]
            bool AddressBar { get; set; }
            [DispId(0x22c)]
            bool Resizable { get; set; }
        }
   }