常见异常

本指南介绍在使用 DotNetBrowser 库时可能抛出的常见异常。

初始化异常

许可证异常

当许可证检查由于某种原因失败时会抛出这些异常。

  • NoLicenseException 在缺少许可证时抛出 。 这种情况通常发生在 EngineOptions 中没有设置许可证密钥,以及 DotNetBrowser 无法找到包含许可证密钥的 dotnetbrowser.license 文件时。 要解决这种情况,请检查许可证是否已添加到项目中。
  • InvalidLicenseException 在许可证配置正确但检查失败时抛出。 许可证检查失败的确切原因作为异常消息提供。 例如,当许可证过期或许可证中的产品绑定与任何命名空间不匹配时,可能会抛出此异常。

例如,可能出现的 InvalidLicenseException 消息之一:DotNetBrowser 许可证检查失败。 原因:此产品版本与许可证不兼容。` 这意味着项目中指定的许可证密钥与引用的 DotNetBrowser 版本不兼容。 您需要确保使用正确且有效的密钥。

如若非上述情况,请随时联系我们的销售团队 sales@teamdev.com 以获取更新的许可证。

Chromium 二进制文件异常

当 DotNetBrowser 由于 Chromium 二进制文件丢失且无法恢复而无法启动绑定的 Chromium 引擎时,将抛出 ChromiumBinariesMissingException 。 异常的实际原因作为异常消息提供。 例如,当在 Chromium 二进制文件目录中找不到兼容的 Chromium 二进制文件,并且 DotNetBrowser 找不到包含兼容二进制文件的程序集时,可能会抛出此异常。

在单独进程中提取 Chromium 二进制文件失败

当在程序集中找到 Chromium 二进制文件,但在将其提取到 Chromium 二进制目录时出现问题时,将引发此异常。

在大多数情况下,从 DLL 中提取二进制文件是通过 GZipCompress.exe 执行的,该实用程序是 DotNetBrowser 的一部分。 由于某些原因,此实用程序可能无法提取二进制文件。 例如,它被第三方应用程序 (防病毒软件或防火墙) 阻止或缺少此操作的权限。 在这种情况下会出现以下异常消息:`Failed to extract the Chromium binaries in the separate process.GZipCompress.exe 已退出,代码为

发生此异常时, GZipCompress.exe 的详细输出将写入 DotNetBrowser 日志。 检查这些日志通常有助于了解故障的实际原因。

其他引擎初始化异常

EngineInitializationException 在初始化 IEngine 实例期间出现错误时抛出。

用户数据目录已被使用

即使在不同的.NET进程中创建这些 IEngine 实例,也不允许创建指向相同用户数据目录的两个或多个 IEngine 实例。 Chromium 本身不支持这种用例,它会导致不可预测的行为和引擎突然崩溃。

因此,当检测到这种情况时,将抛出 EngineInitializationException 并显示以下消息:The user data directory is already in use: <path>

有几种可能的解决方案:

  1. 使用同一个 IEngine 创建多个 IBrowser 实例:
     IEngine engine = EngineFactory.Create(engineOptions);
    
     IBrowser browser1 = engine.CreateBrowser();
     IBrowser browser2 = engine.CreateBrowser();
    
     Dim engine As IEngine = EngineFactory.Create(engineOptions)
    
     Dim browser1 As IBrowser = engine.CreateBrowser()
     Dim browser2 As IBrowser = engine.CreateBrowser()
    
  2. 使用不同的用户数据目录创建单独的 IEngine 实例:
     IEngine engine1 = EngineFactory.Create(new EngineOptions.Builder
     {
         UserDataDirectory = @"C:\Users\Me\DotNetBrowser1"
     }.Build());
    
     IEngine engine2 = EngineFactory.Create(new EngineOptions.Builder
     {
         UserDataDirectory = @"C:\Users\Me\DotNetBrowser2"
     }.Build());
    
     IBrowser browser1 = engine1.CreateBrowser();
     IBrowser browser2 = engine2.CreateBrowser();
    
     Dim engine1 As IEngine = EngineFactory.Create(New EngineOptions.Builder With 
     {
         .UserDataDirectory = "C:\Users\Me\DotNetBrowser"
     }.Build())
    
     Dim engine2 As IEngine = EngineFactory.Create(New EngineOptions.Builder With 
     {
         .UserDataDirectory = "C:\Users\Me\DotNetBrowser"
     }.Build())
    
     Dim browser1 As IBrowser = engine1.CreateBrowser()
     Dim browser2 As IBrowser = engine2.CreateBrowser()
    

运行时异常

ObjectDisposedException

ObjectDisposedException 在尝试使用已被处理的对象时抛出。 例如,在处理以下内容时会抛出此异常:

  • 在加载不同的网页后,尝试使用之前缓存的 DOM 元素或 JavaScript 对象;
  • 浏览器中已不存在的某个框架;
  • 在调用 IEngine.Dispose() 后,尝试使用 IEngine 实例,任何由此引擎创建的 IBrowser 实例或与这些实例相关的任何其他对象。
  • Chromium 崩溃之后,尝试使用 IEngine 实例,任何由此引擎创建的 IBrowser 实例或与这些实例相关的任何其他对象。

ConnectionClosedException

ConnectionClosedException 在与 Chromium 引擎的连接关闭时抛出。 此异常通常与 IEngine.Dispose() 方法调用Chromium 崩溃有关。

跨线程操作无效

当您尝试在 DotNetBrowser 事件上修改 UI 时,您会看到 System.InvalidOperationException 以及以下描述: Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

DotNetBrowser 通过单独的线程触发其事件,并且不为此目的使用主 UI 线程。 要在 DotNetBrowser 事件上修改 UI,请使用 WinForms 中的 InvokeBeginInvoke 或 WPF 中的 Dispatcher.InvokeDispatcher.BeginInvoke 将执行传递到主 UI 线程。 WinForms 中的 Control.InvokeRequired 属性或 WPF 中的 Dispatcher.CheckAccess() 调用可用于检查是否有必要通过执行。

下面的代码示例演示了如何从另一个线程访问 UI。

WinForms

browser.Navigation.FrameLoadFinished += (s, e) =>
{
    if (e.Frame.IsMain)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new Action(() =>
            {
                this.Title = e.Browser.Url;
            }));
        }
        else
        {
            this.Title = e.Browser.Url;
        }
    }
};
browser.Navigation.LoadUrl("https://www.teamdev.com");
AddHandler browser.Navigation.FrameLoadFinished, Sub(s, e)
    If e.Frame.IsMain Then
        If InvokeRequired Then
            BeginInvoke(New Action(Sub()
                Me.Title = e.Browser.Url
            End Sub))
        Else
            Me.Title = e.Browser.Url
        End If
    End If
End Sub
browser.Navigation.LoadUrl("https://www.teamdev.com")

WPF

browser.Navigation.FrameLoadFinished += (s, e) =>
{
    if (e.Frame.IsMain)
    {
        if (!Dispatcher.CheckAccess())
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                this.Title = e.Browser.Url;
            }));
        }
        else
        {
            this.Title = e.Browser.Url;
        }
    }
};
browser.Navigation.LoadUrl("https://www.teamdev.com");
AddHandler browser.Navigation.FrameLoadFinished, Sub(s, e)
    If e.Frame.IsMain Then
        If Not Dispatcher.CheckAccess() Then
            Dispatcher.BeginInvoke(New Action(Sub()
                Me.Title = e.Browser.Url
            End Sub))
        Else
            Me.Title = e.Browser.Url
        End If
    End If
End Sub
browser.Navigation.LoadUrl("https://www.teamdev.com")

Avalonia UI

browser.Navigation.FrameLoadFinished += (s, e) =>
{
    if (e.Frame.IsMain)
    {
        if (!Dispatcher.UIThread.CheckAccess())
        {
            Dispatcher.UIThread.InvokeAsync(new Action(() =>
            {
                this.Title = e.Browser.Url;
            }));
        }
        else
        {
            this.Title = e.Browser.Url;
        }
    }
};
browser.Navigation.LoadUrl("https://www.teamdev.com");
AddHandler browser.Navigation.FrameLoadFinished, Sub(s, e)
    If e.Frame.IsMain Then
        If Not Dispatcher.UIThread.CheckAccess() Then
            Dispatcher.UIThread.InvokeAsync(New Action(Sub()
                Me.Title = e.Browser.Url
            End Sub))
        Else
            Me.Title = e.Browser.Url
        End If
    End If
End Sub
browser.Navigation.LoadUrl("https://www.teamdev.com")
Go Top