WPF WebBrowser control - how to suppress script errors?

56,072

Solution 1

Here is a C# routine that is capable of putting WPF's WebBrowser in silent mode. You can't call it at WebBrowser initialization as it 's too early, but instead after navigation occured. Here is a WPF sample app with a wbMain WebBrowser component:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        wbMain.Navigated += new NavigatedEventHandler(wbMain_Navigated);
    }

    void wbMain_Navigated(object sender, NavigationEventArgs e)
    {
        SetSilent(wbMain, true); // make it silent
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        wbMain.Navigate(new Uri("... some url..."));
    }
}


public static void SetSilent(WebBrowser browser, bool silent)
{
    if (browser == null)
        throw new ArgumentNullException("browser");

    // get an IWebBrowser2 from the document
    IOleServiceProvider sp = browser.Document as IOleServiceProvider;
    if (sp != null)
    {
        Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
        Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");

        object webBrowser;
        sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser);
        if (webBrowser != null)
        {
            webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent });
        }
    }
}


[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IOleServiceProvider
{
  [PreserveSig]
  int QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
}

Solution 2

Thought it'd be relevant to add here. There is another option to get to the WPF WebBrowser's underlying WebBorwser ActiveX Control and its otherwise inaccessible methods and events. I just discovered it a few days ago. It's very simple and doesn't require initial navigation on WB:

dynamic activeX = this.WB.GetType().InvokeMember("ActiveXInstance",
                    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                    null, this.WB, new object[] { });

activeX.Silent = true;

Of course, there's a chance this method may not work in future version of the Framework, but so is true about any other undocumented method. So far, it's been there since .NET 3.0. More details with a working code sample here.

Solution 3

Thanks to Simon Mourier for elegant way to solve this problem. I made a little improvement and encapsulated Simon's solution into attached property.

In my application I use WebBrowser control databounded to viewmodel, the webbrowser might be hidden on inactive TabItem, so I have to check that it has been Loaded and Navigated before setting javascript errors silent. And of cource this setting should be done just once, so after setting I release hooked events.

XAML Code:

<TabControl xmlns:b="clr-namespace:MyApplication.Behaviors">
  <TabItem Header="foo">...</TabItem>
  <TabItem Header="Google map">
    <WebBrowser b:BindableSource="{Binding Path=MapUrl}"
                b:DisableJavascriptErrors="True" />
  </TabItem>
</TabControl>

Behavior code:

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace MyApplication.Behaviors
{
    public class WebBrowserBehavior
    {
        private static readonly Type OwnerType = typeof (WebBrowserBehavior);

        #region BindableSource

        public static readonly DependencyProperty BindableSourceProperty =
            DependencyProperty.RegisterAttached(
                "BindableSource", 
                typeof(string), 
                OwnerType, 
                new UIPropertyMetadata(OnBindableSourcePropertyChanged));

        [AttachedPropertyBrowsableForType(typeof(WebBrowser))]
        public static string GetBindableSource(DependencyObject obj)
        {
            return (string)obj.GetValue(BindableSourceProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(WebBrowser))]
        public static void SetBindableSource(DependencyObject obj, string value)
        {
            obj.SetValue(BindableSourceProperty, value);
        }

        public static void OnBindableSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var browser = d as WebBrowser;
            if (browser == null) return;

            browser.Source = (e.NewValue != null) ? new Uri(e.NewValue.ToString()) : null;
        }

        #endregion

        #region DisableJavascriptErrors

        #region SilentJavascriptErrorsContext (private DP)

        private static readonly DependencyPropertyKey SilentJavascriptErrorsContextKey =
            DependencyProperty.RegisterAttachedReadOnly(
                "SilentJavascriptErrorsContext",
                typeof (SilentJavascriptErrorsContext),
                OwnerType,
                new FrameworkPropertyMetadata(null));

        private static void SetSilentJavascriptErrorsContext(DependencyObject depObj, SilentJavascriptErrorsContext value)
        {
            depObj.SetValue(SilentJavascriptErrorsContextKey, value);
        }

        private static SilentJavascriptErrorsContext GetSilentJavascriptErrorsContext(DependencyObject depObj)
        {
            return (SilentJavascriptErrorsContext) depObj.GetValue(SilentJavascriptErrorsContextKey.DependencyProperty);
        }

        #endregion

        public static readonly DependencyProperty DisableJavascriptErrorsProperty =
            DependencyProperty.RegisterAttached(
                "DisableJavascriptErrors",
                typeof (bool),
                OwnerType,
                new FrameworkPropertyMetadata(OnDisableJavascriptErrorsChangedCallback));

        [AttachedPropertyBrowsableForType(typeof(WebBrowser))]
        public static void SetDisableJavascriptErrors(DependencyObject depObj, bool value)
        {
            depObj.SetValue(DisableJavascriptErrorsProperty, value);
        }

        [AttachedPropertyBrowsableForType(typeof(WebBrowser))]
        public static bool GetDisableJavascriptErrors(DependencyObject depObj)
        {
            return (bool)depObj.GetValue(DisableJavascriptErrorsProperty);
        }

        private static void OnDisableJavascriptErrorsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var webBrowser = d as WebBrowser;
            if (webBrowser == null) return;
            if (Equals(e.OldValue, e.NewValue)) return;

            var context = GetSilentJavascriptErrorsContext(webBrowser);
            if (context != null) {
                context.Dispose();
            }

            if (e.NewValue != null) {
                context = new SilentJavascriptErrorsContext(webBrowser);
                SetSilentJavascriptErrorsContext(webBrowser, context);
            }
            else {
                SetSilentJavascriptErrorsContext(webBrowser, null);
            }
        }

        private class SilentJavascriptErrorsContext : IDisposable
        {
            private bool? _silent; 
            private readonly WebBrowser _webBrowser;


            public SilentJavascriptErrorsContext(WebBrowser webBrowser)
            {
                _silent = new bool?();

                _webBrowser = webBrowser;
                _webBrowser.Loaded += OnWebBrowserLoaded;
                _webBrowser.Navigated += OnWebBrowserNavigated;
            }

            private void OnWebBrowserLoaded(object sender, RoutedEventArgs e)
            {
                if (!_silent.HasValue) return;

                SetSilent();
            }

            private void OnWebBrowserNavigated(object sender, NavigationEventArgs e)
            {
                var webBrowser = (WebBrowser)sender;

                if (!_silent.HasValue) {
                    _silent = GetDisableJavascriptErrors(webBrowser);
                }

                if (!webBrowser.IsLoaded) return;

                SetSilent();
            }

            /// <summary>
            /// Solution by Simon Mourier on StackOverflow
            /// http://stackoverflow.com/a/6198700/741414
            /// </summary>
            private void SetSilent()
            {
                _webBrowser.Loaded -= OnWebBrowserLoaded;
                _webBrowser.Navigated -= OnWebBrowserNavigated;

                // get an IWebBrowser2 from the document
                var sp = _webBrowser.Document as IOleServiceProvider;
                if (sp != null)
                {
                    var IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
                    var IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");

                    object webBrowser2;
                    sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser2);
                    if (webBrowser2 != null)
                    {
                        webBrowser2.GetType().InvokeMember(
                            "Silent",
                            BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty,
                            null,
                            webBrowser2,
                            new object[] { _silent });
                    }
                }
            }

            [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            private interface IOleServiceProvider
            {
                [PreserveSig]
                int QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
            }

            public void Dispose()
            {
                if (_webBrowser != null) {
                    _webBrowser.Loaded -= OnWebBrowserLoaded;
                    _webBrowser.Navigated -= OnWebBrowserNavigated;
                }
            }
        }

        #endregion

    }
}

Solution 4

The answer by @SimonMourier didn't work for me, but this did:

public void HideScriptErrors(WebBrowser wb, bool Hide)
{
    FieldInfo fiComWebBrowser = typeof(WebBrowser)
        .GetField("_axIWebBrowser2", 
                  BindingFlags.Instance | BindingFlags.NonPublic);
    if (fiComWebBrowser == null) return;
    object objComWebBrowser = fiComWebBrowser.GetValue(wb);
    if (objComWebBrowser == null) return;
    objComWebBrowser.GetType().InvokeMember(
        "Silent", BindingFlags.SetProperty, null, objComWebBrowser, 
        new object[] { Hide });
}

Note that I got it from here.

Solution 5

Well if it was necessary i would have gone for WinformHost and added WebBrowser Control to it and used it.

You can easily do those tasks here in WinformHost also because i have made whole applications that does bundle of things

Share:
56,072
DrLazer
Author by

DrLazer

Software Engineer, particularly interested in - .net and Microsoft technologies - Test driven development - Extreme programming - Agile software development - Process improvement and development efficiency Also a photography nerd

Updated on November 22, 2021

Comments

  • DrLazer
    DrLazer over 2 years

    I found a similar question here:

    How do I suppress script errors when using the WPF WebBrowser control?

    But non of those solutions work for me. I need to stop the popups from appearing as i am using the WebBrowser to automate admin tasks on a website.

    SuppressScriptErrors does not appear to be an available attribute on my WebControl :(

  • DrLazer
    DrLazer almost 13 years
    Thankyou Simon. What the heck is going off with that code? It works a charm, but I cannot read it that well.
  • Simon Mourier
    Simon Mourier almost 13 years
    @DrLazer - Thanks. It uses ancient COM/IE knowledge :-)
  • Afnan Bashir
    Afnan Bashir almost 13 years
    thanks but no thanks for downvote but i do not see any reason to down vote.I gave what is possible solution.
  • DrLazer
    DrLazer almost 13 years
    clicked by mistake. my appologies
  • DrLazer
    DrLazer almost 13 years
    ahhh wont let me undo it. :(
  • andreapier
    andreapier about 12 years
    This is great, i've been looking for this since long time! Thank you very much!!
  • Asti
    Asti about 12 years
    @DrLazer There used to be a property on the old IE COM control called Silent. If you set Silent to true, it would prevent the control from creating any windows. MS didn't port all the properties to WPF (WinForms has it much better), so you have to resort to hacks like these to actually set the property in late binding.
  • Keven M
    Keven M over 11 years
    I'm still a little shaky on bindings, so what does the MapUrl in your XAML binding refer to. I implemented this exactly as you have here, including the MapUrl binding, and everything works. But I don't have a MapUrl variable/object anywhere, so I'd kind of like to know how exactly this is working. :)
  • MUG4N
    MUG4N over 10 years
    works great, but I had to change this line: IOleServiceProvider sp = browser.Document.DomDocument as IOleServiceProvider;
  • Simon Mourier
    Simon Mourier over 10 years
    DomDocument is for Windows forms, isn't it? We were talking about WPF here. Windows forms's webbrowser object already has a ScriptErrorsSuppressed property available.
  • Rameez Ahmed Sayad
    Rameez Ahmed Sayad over 10 years
    Wow awesome piece of code... just those days when you know the true power of COM(read compilable code) :)
  • Mikkel Løkke
    Mikkel Løkke about 9 years
    This worked like a charm, and is really elegant. Thanks.
  • JWP
    JWP about 9 years
    @Noseratio Yea! Finally a two liner.... I still don't understand why Microsoft doesn't make this a public method named Silence or something like that. Imagine this: XWB.Silent = true;
  • Titwan
    Titwan almost 9 years
    All the other solutions did not work for me !!! Except that solution with ActiveXInstance ! It works really well. Thanks a lo. I broke my nerves on that issue for long. Caution :: to place the code into the MainWindow constructor.
  • Gopichandar
    Gopichandar over 8 years
    Works. Thanks for that.
  • curiousity
    curiousity over 8 years
    What a nice piece of a code - it saved me a tone of nerves
  • Boogier
    Boogier over 6 years
    This woks great!
  • Shivani Katukota
    Shivani Katukota almost 6 years
    Has something changed in the latest .net version??.this stopped working for me now :( used to work before!
  • Rao Adnan
    Rao Adnan over 5 years
    Great. Works for me
  • vinsa
    vinsa over 5 years
    I use this right after initiating the WebBrowser, it works for my WPF app.