web-dev-qa-db-fra.com

Contrôle WPF WebBrowser - comment supprimer les erreurs de script?

J'ai trouvé une question similaire ici:

Comment supprimer les erreurs de script lors de l'utilisation du contrôle WPF WebBrowser?

Mais aucune de ces solutions ne fonctionne pour moi. Je dois empêcher les fenêtres contextuelles d'apparaître lorsque j'utilise WebBrowser pour automatiser les tâches d'administration sur un site Web.

SuppressScriptErrors ne semble pas être un attribut disponible sur mon WebControl :(

55
DrLazer

Voici une routine C # capable de mettre WebBrowser de WPF en mode silencieux. Vous ne pouvez pas l'appeler lors de l'initialisation de WebBrowser car il est trop tôt, mais à la place après la navigation. Voici un exemple d'application WPF avec un composant wbMain WebBrowser:

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);
}
109
Simon Mourier

J'ai pensé qu'il serait pertinent d'ajouter ici. Il existe une autre option pour accéder au WPF WebBrowser sous-jacent WebBorwser ActiveX Control et son autrement inaccessible méthodes et événements =. Je viens de le découvrir il y a quelques jours. C'est très simple et ne nécessite pas de navigation initiale sur WB:

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

activeX.Silent = true;

Bien sûr, il est possible que cette méthode ne fonctionne pas dans la future version du Framework, mais il en va de même pour toute autre méthode non documentée. Jusqu'à présent, il existe depuis .NET 3.0. Plus de détails avec un exemple de code fonctionnel ici .

48
noseratio

Merci à Simon Mourier pour la manière élégante de résoudre ce problème. J'ai fait une petite amélioration et j'ai encapsulé la solution de Simon dans la propriété attachée.

Dans mon application, j'utilise WebBrowser control databounded pour afficher le modèle, le webbrowser peut être masqué sur TabItem inactif, je dois donc vérifier qu'il a été Chargé et Navigué avant de définir des erreurs javascript silencieux. Et bien sûr, ce paramètre ne doit être effectué qu'une seule fois, donc après la configuration, je libère les événements liés.

Code XAML:

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

Code de comportement:

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

    }
}
14
Ondrej

La réponse de @SimonMourier n'a pas fonctionné pour moi, mais cela a:

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 });
}

Notez que je l'ai obtenu de ici .

12
Scott Whitlock

J'ai également trouvé un moyen intéressant de désactiver les erreurs JavaScript. Mais vous devez utiliser au moins .Net Framework 4.0 en raison de l'utilisation d'un type dynamique élégant.

Vous devez vous abonner à l'événement LoadCompleted de l'élément WebBrowser:

<WebBrowser x:Name="Browser" 
            LoadCompleted="Browser_OnLoadCompleted" />

Après cela, vous devez écrire un gestionnaire d'événements qui ressemble à ci-dessous:

    void Browser_OnLoadCompleted(object sender, NavigationEventArgs e)
    {
        var browser = sender as WebBrowser;

        if (browser == null || browser.Document == null)
            return;

        dynamic document = browser.Document;

        if (document.readyState != "complete")
            return;

        dynamic script = document.createElement("script");
        script.type = @"text/javascript";
        script.text = @"window.onerror = function(msg,url,line){return true;}";
        document.head.appendChild(script);
    }
4
Ashot Muradian

Vous pouvez regarder ma solution: Désactiver les erreurs JavaScript dans le contrôle WebBrowser dans Silverlight - c'est pour Silverlight mais devrait également fonctionner pour WPF.

1
Rafal Spacjer

Eh bien, si c'était nécessaire, j'aurais opté pour WinformHost et y aurais ajouté le contrôle WebBrowser et l'aurais utilisé.

Vous pouvez facilement effectuer ces tâches ici dans WinformHost aussi parce que j'ai créé des applications entières qui regroupent des choses

1
Afnan Bashir

Voici un exemple de la façon de procéder sans utiliser la réflexion.

    /// <summary>
    /// Gets an interop web browser.
    /// </summary>
    /// <param name="browser"></param>
    /// <returns></returns>
    public static SHDocVw.WebBrowser GetInteropWebBrowser(this WebBrowser browser)
    {
        Guid serviceGuid = new Guid("0002DF05-0000-0000-C000-000000000046");
        Guid iid = typeof(SHDocVw.IWebBrowser2).GUID;
        Interop.IServiceProvider serviceProvider = (Interop.IServiceProvider)browser.Document;
        SHDocVw.IWebBrowser2 browser2 = (SHDocVw.IWebBrowser2)serviceProvider.QueryService(ref serviceGuid, ref iid);
        SHDocVw.WebBrowser wb = (SHDocVw.WebBrowser)browser2;

        return wb;
    }

    /// <summary>
    /// Disables script errors for the browser.
    /// </summary>
    /// <param name="browser"></param>
    /// <param name="silent"></param>
    public static void SetSilent(this WebBrowser browser, bool silent)
    {
        SHDocVw.WebBrowser browser2 = browser.GetInteropWebBrowser();
        if (browser2 != null)
            browser2.Silent = silent;
    }

/// <summary>
/// Provides the COM interface for the IServiceProvider.
/// </summary>
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
    /// <summary>
    /// Queries the service.
    /// </summary>
    /// <param name="serviceGuid"></param>
    /// <param name="riid"></param>
    /// <returns></returns>
    [return: MarshalAs(UnmanagedType.IUnknown)]
    object QueryService(ref Guid serviceGuid, ref Guid riid);
}

Ensuite, dans le constructeur de la vue qui héberge le contrôle du navigateur que vous avez:

            Browser.Navigated += (s, e) =>
            {
                Browser.SetSilent(true);
            };
1
Icons of Industry