当使用WPF WebBrowser控件时,我该如何抑制脚本错误?

55

我有一个 WPF 应用程序,使用 WPF WebBrowser 控件在平板电视上展示有趣的网页(类似于新闻源)。

问题是,我偶尔会收到 HTML 脚本错误提示,弹出令人讨厌的 IE 错误消息,询问是否要“停止运行此页上的脚本”。有没有方法可以抑制此错误检查?

注意:我已经在 IE 设置中禁用了脚本调试。


1
又来了一个两行代码的解决方案:https://dev59.com/um025IYBdhLWcg3wOzPX#18289217 - noseratio - open to work
9个回答

140

这是我刚刚用反射创建的解决方案。解决了问题 :) 我将它在Navigated事件中运行,因为在那之前似乎ActiveX对象不可用。

它所做的是在基础ActiveX对象上设置.Silent属性。这与.ScriptErrorsSuppressed属性相同,后者是Windows窗体的等效属性。

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

一个更好的版本,可以在任何时候运行,而不是在 ".Navigated" 事件之后:

public void HideScriptErrors(WebBrowser wb, bool hide) {
    var fiComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
    if (fiComWebBrowser == null) return;
    var objComWebBrowser = fiComWebBrowser.GetValue(wb);
    if (objComWebBrowser == null) {
        wb.Loaded += (o, s) => HideScriptErrors(wb, hide); //In case we are to early
        return;
    }
    objComWebBrowser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, null, objComWebBrowser, new object[] { hide });
}

如果第二个示例存在任何问题,请尝试将 wb.Loaded 与 wb.Navigated 交换。


+1 感谢您的分享 - 我真的不想使用“WindowsFormsHost”来实现此功能,您的代码似乎能够很好地完成这项工作。 - Sheridan
你如何将此添加到WebBrowser对象? - Furkan Gözükara
5
var wb = new WebBrowser(); wb.Navigated += (a,b)=>{ HideScriptErrors(wb, true); }; 这段代码只需要运行一次,但你可以了解一下。在Navigated事件被触发之前,不能设置该属性。请注意不改变原意并使内容更加通俗易懂,不提供其他信息。 - Wolf5
创新!谢谢! - Rich
太棒了!你怎么做出来的 :) - Boppity Bop

17

刚从另一个问题中发现,这个方法非常优雅且有效。

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

activeX.Silent = true;

1
我将这个代码放在 WebBrowser_OnNavigating 事件下,它可以正常工作!+1 - Mike Keskinov

10

这里的问题是WPF中的WebBrowser没有像2.0控件那样实现此属性。

您最好在WPF应用程序中使用WindowsFormsHost,并使用2.0的WebBrowser属性:SuppressScriptErrors。即使这样,您也需要将应用程序设置为完全信任才能执行此操作。

虽然不算理想,但目前几乎是唯一的选择。


现在我有一个反对那些讨厌winforms的人的理由。因为有太多的“不可能”。 - C4d

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

在我的情况下,我需要修复的代码是将完整的标记作为MemoryStream创建,并使用NavigateToStream将其传递给浏览器。因此,解决方案很简单,只需添加一个window.onerror函数来静默处理错误即可。谢谢@Ashot Muradian。 - pasx
在99%的情况下,ActiveX的技巧已经足够并且有效,但我只有一个案例,这是隐藏JS错误的唯一选择。谢谢! - Edi

6
我想作为评论添加到@Alkampfer的答案中,但是我没有足够的声望。这对我有用(Windows 8.1,NET 4.5):
 window.Browser.LoadCompleted.Add(fun _ -> 
    window.Browser.Source <- new System.Uri("javascript:window.onerror=function(msg,url,line){return true;};void(0);"))

这个代码示例是用F#编写的,但它的功能非常明显。


3
检查下面的代码,以抑制WPF浏览器控件中的脚本错误。
    public MainWindow
    {
    InitializeComponent();
    WebBrowserControlView.Navigate(new Uri("https://www.hotmail.com"));
                        //The below checks for script errors.
    ViewerWebBrowserControlView.Navigated += ViewerWebBrowserControlView_Navigated;
    }

void ViewerWebBrowserControlView_Navigated(object sender, NavigationEventArgs e)
            {
    BrowserHandler.SetSilent(ViewerWebBrowserControlView, true); // make it silent
            }

public static class BrowserHandler
{
    private const string IWebBrowserAppGUID = "0002DF05-0000-0000-C000-000000000046";
    private const string IWebBrowser2GUID = "D30C1661-CDAF-11d0-8A3E-00C04FC9E26E";

    public static void SetSilent(System.Windows.Controls.WebBrowser browser, bool silent)
    {
        if (browser == null)
            MessageBox.Show("No Internet Connection");

        // get an IWebBrowser2 from the document
        IOleServiceProvider sp = browser.Document as IOleServiceProvider;
        if (sp != null)
        {
            Guid IID_IWebBrowserApp = new Guid(IWebBrowserAppGUID);
            Guid IID_IWebBrowser2 = new Guid(IWebBrowser2GUID);

            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)]
public interface IOleServiceProvider
{
    [PreserveSig]
    int QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);


   }

如果您正在使用Winforms Web浏览器与winforms主机,您需要设置一个名为“SuppressScriptErrors”的属性,并将其设置为true。

    <WindowsFormsHost Name="WinformsHost" Grid.Row="1">
    <winForms:WebBrowser x:Name="WebBrowserControlView" ScriptErrorsSuppressed="True" AllowWebBrowserDrop="False"></winForms:WebBrowser>
</WindowsFormsHost>

5
当你复制现有的代码时,请参考**原始帖子**。 - noseratio - open to work

2

实际上,我已经在生产程序中使用了8.1和4.5版本的代码运行成功,但该代码可能已从示例中更改。如果您有兴趣,我可以尝试验证新代码并发布更新的文章。 - Alkampfer

1

你可以使用这个技巧

vb.net

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Private Const WM_CLOSE As Short = &H10s

并调用最后一个库:

 dim hwnd 
    dim vreturnvalue
    hwnd = FindWindow(vbNullString,"script error")
    if hwnd<>0 then vreturnvalue = SendMessage(hwnd, WM_CLOSE,  &O0s, &O0s)

1
Wolf5的回答很好,但是if (objComWebBrowser == null)似乎不起作用。相反,我检查WebBrowser实例的IsLoaded属性:
public void HideScriptErrors(WebBrowser webBrowser)
{
    if (!webBrowser.IsLoaded)
    {
        webBrowser.Loaded += WebBrowser_Loaded; // in case we are too early
        return;
    }

    var objComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(webBrowser);
    if (objComWebBrowser == null)
    {
        return;
    }

    objComWebBrowser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, null, objComWebBrowser, new object[] { true });
}
private void WebBrowser_Loaded(object sender, RoutedEventArgs e)
{
    var webBrowser = sender as WebBrowser;
    webBrowser.Loaded -= WebBrowser_Loaded;
    HideScriptErrors(webBrowser);
}

在第一次之后,有必要移除Loaded事件处理程序,因为当通过切换到不同的选项卡使其不可见时,控件可能会多次卸载和加载。此外,当WebBrowser在第一次导航时尚未可见时,例如它位于应用程序启动时不可见的不同选项卡上时,if (!wb.Loaded))回退仍然很重要。


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