WPF WebBrowser控件-如何抑制脚本错误?

65

我在这里找到了一个类似的问题:

如何在使用WPF WebBrowser控件时禁止脚本错误?

但是那些解决方案都对我不起作用。我需要停止弹出窗口的出现,因为我正在使用WebBrowser在网站上自动化管理任务。

SuppressScriptErrors 在我的WebControl中似乎没有可用属性 :(


1
WinForms、WPF 还是 Compact Framework? - abatishchev
抱歉,我忘记提到了 - 它是 WPF。 - DrLazer
10个回答

122

这是一个能够将WPF的WebBrowser设置为静默模式的C#例程。你不能在WebBrowser初始化时调用它,因为那时太早了,而是在导航发生后调用。以下是一个带有wbMain WebBrowser组件的WPF示例应用程序:

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

1
谢谢你,Simon。那段代码到底是怎么回事?它能正常运行,但我看不太懂。 - DrLazer
5
@DrLazer - 谢谢。它使用古老的COM/IE知识 :-) - Simon Mourier
2
太好了,我已经寻找这个很长时间了!非常感谢!! - andreapier
1
@DrLazer 在旧的IE COM控件中,曾经有一个名为Silent的属性。如果将Silent设置为true,它将阻止控件创建任何窗口。微软没有将所有属性移植到WPF(WinForms做得更好),因此您必须采用类似这样的黑客方式来在后期绑定中实际设置该属性。 - Asti
工作得很好,但我不得不更改这一行:IOleServiceProvider sp = browser.Document.DomDocument as IOleServiceProvider; - MUG4N
显示剩余3条评论

61

提一下还有另一个方法可以获取到WPF WebBrowser的底层WebBorwser ActiveX Control及其平时无法访问的方法事件。我几天前才发现这个方法。它非常简单,不需要在WB上进行初始导航:

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

activeX.Silent = true;

当然,这种方法在未来版本的Framework中可能不再有效,但对于任何其他未记录的方法也是如此。到目前为止,它已经存在自.NET 3.0以来。 有了一个工作代码示例的更多细节在这里


2
这个代码非常完美,而且非常优雅。谢谢。 - Mikkel Løkke
1
@Noseratio 是啊!终于只有两行代码了.... 我还是不明白为什么微软不把这个命名为公共方法,比如 Silence 之类的。 想象一下:XWB.Silent = true; - JWP
1
所有其他的解决方案对我都不起作用!!!除了使用ActiveXInstance的解决方案!它真的很有效。非常感谢。我在这个问题上烦恼了很长时间。 注意:将代码放置在MainWindow构造函数中。 - Titwan
这个很棒! - Boogier
我在初始化WebBrowser后立即使用它,它适用于我的WPF应用程序。 - vinsa

15

感谢Simon Mourier提供了解决这个问题的优雅方式。我进行了一些改进,并将Simon的解决方案封装成了附加属性。

在我的应用程序中,我使用了绑定到ViewModel的WebBrowser控件,WebBrowser可能会隐藏在非活动的TabItem上,因此在设置JavaScript错误静默之前,我必须检查它是否已经加载和导航。当然,这个设置只需要做一次,所以设置完成后,我释放了挂钩的事件。

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>

行为代码:

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
            /// https://dev59.com/um025IYBdhLWcg3wOzPX#6198700
            /// </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

    }
}

3
我对绑定还有点不熟悉,所以你在XAML绑定中使用的MapUrl是指什么?我完全按照你这里的实现方式进行了操作,包括MapUrl绑定,一切都运行良好。但是我没有任何MapUrl变量/对象存在,所以我想知道这到底是如何工作的 :) - Keven M
最新的.NET版本有什么变化吗??这现在对我不起作用:(以前可以工作! - Shivani Katukota

13

@SimonMourier的回答对我没用,但是这个有用:

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

请注意,我是从这里获取的。


工作正常。谢谢你的帮助。 - Gopichandar
太好了。适用于我。 - Rao Adnan

7

我还发现了一种有趣的方法来禁用JavaScript错误。但是,由于使用优雅的动态类型,您需要至少使用.Net Framework 4.0。

您需要订阅WebBrowser元素的LoadCompleted事件:

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

接下来需要编写一个事件处理程序,如下所示:

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

1

如果必要的话,我会选择WinformHost并将WebBrowser控件添加到其中并使用它。

在这里,您也可以轻松完成这些任务,因为我已经制作了整个应用程序来执行许多任务。


谢谢你的反对,但我没有看到任何理由进行反对。我提供了可能的解决方案。 - Afnan Bashir
误点了。我道歉。 - DrLazer
啊,它不让我撤销。:( - DrLazer

1
这里是一个不使用反射的示例。
    /// <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);
}

然后在承载浏览器控件的视图的构造函数中,您需要:

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

0

我想补充一下,我尝试了上述所有解决方案来尝试停止长时间运行的脚本错误(虽然它们并不声称能够这样做,但这是我能找到的最接近的问题)。发帖是为了提醒其他人是否有相同的“此页面上的脚本正在使您的浏览器运行缓慢”的问题。

我唯一发现有效的方法是设置注册表键,必须在创建浏览器之前设置。

        var keyName = "HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\styles";
        if(Registry.GetValue(keyName, "MaxScriptStatements", null) == null)
        {
            Registry.SetValue(keyName, "MaxScriptStatements", unchecked((int)0xffffffff), RegistryValueKind.DWord);
        }

enter image description here


0
        wbSample.Navigating += (s, e) =>
            {
                var fiComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                if (fiComWebBrowser == null)
                    return;

                object objComWebBrowser = fiComWebBrowser.GetValue(wbSample);
                if (objComWebBrowser == null)
                    return;

                objComWebBrowser.GetType().InvokeMember("Silent", System.Reflection.BindingFlags.SetProperty, null, objComWebBrowser, new object[] { true });
            };


这段代码对我有效。将其添加到构造函数中。

参考:https://social.msdn.microsoft.com/Forums/lync/en-US/8a62fed2-24c8-4765-a6ab-aa245fa7f8d5/wpf-web-browser-script-error?forum=wpf


-3

非常简单,感谢您的解决方案。

http://social.msdn.microsoft.com/Forums/en-US/6996b0c5-b44d-4040-9dbe-6206b1d9185e/webbrowser-script-error-when-using-google-maps?forum=wpf&prof=required


Dim sb As New StringBuilder
sb.Append("<html>")
sb.Append("<head>")
sb.Append("</head")
sb.Append("<body>")

sb.Append("<iframe src ='" + url + "' height='" + webBrowser1.Height + "' width='" + webBrowser1.Width + "'></iframe>")

sb.Append("</body")
sb.Append("</html>")

WebBrowser1.DocumentText = sb.ToString

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接