Question

I'm trying to get at the Close event of the .NET WebBrowser type, which doesn't seem to work out of the box. (EDIT: This event is emitted when the window.close() call is issued in a script running in the browser.)

One solution I've seen is to extend the WebBrowser class and override the WndProc method.

My extension code is as follows:

type internal ExtendedBrowser () = class
    inherit System.Windows.Forms.WebBrowser()

    let WM_PARENTNOTIFY : int = 0x0210
    let WM_DESTROY      : int = 0x0002

    let closed : Event<unit> = new Event<unit>()

    do ()

    [<System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")>]
    override this.WndProc (msg : Message byref) =
        match msg.Msg with
        | wm when wm = WM_PARENTNOTIFY ->
            if (not base.DesignMode) && (msg.WParam.ToInt32() = WM_DESTROY)
            then closed.Trigger()
            base.DefWndProc(ref msg)
        | _ ->
            base.WndProc(ref msg)

    member this.Closed = closed.Publish
end

This ends up causing an exception to be thrown when an instance of the type is accessed:

Unhandled Exception: System.Reflection.TargetInvocationException: Unable to get the window handle for the 'ExtendedBrowser' control. Windowless ActiveX controls are not supported. ---> System.ComponentModel.Win32Exception: Error creating window handle.
   at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
   at System.Windows.Forms.Control.CreateHandle()
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.WebBrowserBase.DoVerb(Int32 verb)
   at System.Windows.Forms.WebBrowserBase.TransitionFromRunningToInPlaceActive()

   --- End of inner exception stack trace ---
   at System.Windows.Forms.WebBrowserBase.TransitionFromRunningToInPlaceActive()

   at System.Windows.Forms.WebBrowserBase.TransitionUpTo(AXState state)
   at System.Windows.Forms.WebBrowser.get_AxIWebBrowser2()
   at System.Windows.Forms.WebBrowser.PerformNavigate2(Object& URL, Object& flag
s, Object& targetFrameName, Object& postData, Object& headers)
   at System.Windows.Forms.WebBrowser.Navigate(String urlString)
   at [PRODUCT].Application.WebBrowser..ctor() in C:\[PATH]\WebBrowser.fs:line 107
   at Program.Main.main(String[] args) in C:\[PATH]\Program.fs:line 79
Press any key to continue . . .

Currently it's erroring on a call to Navigate("about:blank") (the first access of the instance after its construction). I can comment out the WndProc override and things work fine (besides missing the close event).

Someone said you had to put the security attribute on the WndProc override so I did that and it didn't fix things.

Someone else said you can disable the DEP, but I tried and it didn't let me exempt the EXE.

An instance of the extension is being created in a wrapper for the browser (also called WebBrowser) and an instance of this is being created in my main, which is runing in an [STAThread] (which also seems to be required).

Does anyone know what could be going wrong?

(What I'm really after is a way to get notification of the close event, so if someone knows an alternate route to that I'd be happy to hear it.)

Was it helpful?

Solution 2

I found a post where someone else had had the same problem overriding the WndProc method in F#, and the solution there worked for me; working code is as follows:

type ExtendedWebBrowser () = class
    inherit System.Windows.Forms.WebBrowser()

    let WM_PARENTNOTIFY : int = 0x0210
    let WM_DESTROY      : int = 0x0002

    let closed : Event<unit> = new Event<unit>()

    do ()

    override this.WndProc (msg : Message byref) =
        match msg.Msg with
        | wm when wm = WM_PARENTNOTIFY ->
            if (not base.DesignMode) && (msg.WParam.ToInt32() = WM_DESTROY)
            then closed.Trigger()
            base.DefWndProc(&msg)
        | _ ->
            base.WndProc(&msg)

    member this.Closed = closed.Publish
end

It seems that F# treats ref a bit differently than C#: Doing some reading on Parameters and Arguments in F# -- see the Passing by Reference section -- it seems that the ref call copies its argument and uses that copy as the value for the referential cell; so, what I had been passing was a reference cell containing a copy of the contents of msg rather than a reference to the original. The address-of operator (&) gets the address of the value, so that's what was needed for the arguments to the DefWndProc and WndProc methods instead of wrapping them in refs.

OTHER TIPS

If you want to be notified when the WebBrowser control is closed, you might be able to create an instance of the normal WebBrowser class and add an event handler to the HandleDestroyed event -- it's fired when the form/control containing your instance of WebBrowser is closed.

If that doesn't work, you can try casting the Parent property to Form and hooking the FormClosing event; see here for an example of both techniques: HandleDestroyed event in userControl

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top