常见异常
本指南介绍在使用 DotNetBrowser 库时可能抛出的常见异常。
初始化异常
许可证异常
当许可证检查由于某种原因失败时会抛出这些异常。
NoLicenseException在缺少许可证时抛出。这种情况通常发生在EngineOptions中没有设置许可证密钥,以及 DotNetBrowser 无法找到包含许可证密钥的dotnetbrowser.license文件时。要解决这种情况,请检查许可证是否已添加到项目中。InvalidLicenseException在许可证配置正确但检查失败时抛出。许可证检查失败的确切原因作为异常消息提供。例如,当许可证过期或许可证中的产品绑定与任何命名空间不匹配时,可能会抛出此异常。
例如,可能出现的 InvalidLicenseException 消息之一:DotNetBrowser 许可证检查失败。原因:此产品版本与许可证不兼容。这意味着项目中指定的许可证密钥与引用的 DotNetBrowser 版本不兼容。您需要确保使用正确且有效的密钥。
如若非上述情况,请随时联系我们的销售团队 以获取更新的许可证。
Chromium 二进制文件异常
当 DotNetBrowser 由于 Chromium 二进制文件丢失且无法恢复而无法启动绑定的 Chromium engine 时,将抛出 ChromiumBinariesMissingException。异常的实际原因作为异常消息提供。例如,当在 Chromium 二进制文件目录中找不到兼容的 Chromium 二进制文件,并且 DotNetBrowser 找不到包含兼容二进制文件的程序集时,可能会抛出此异常。
在单独进程中提取 Chromium 二进制文件失败
当在程序集中找到 Chromium 二进制文件,但在将其提取到 Chromium 二进制目录时出现问题时,将引发此异常。
在大多数情况下,从 DLL 中提取二进制文件是通过 GZipCompress.exe 执行的,该实用程序是 DotNetBrowser 的一部分。由于某些原因,此实用程序可能无法提取二进制文件。例如,它被第三方应用程序 (防病毒软件或防火墙) 阻止或缺少此操作的权限。在这种情况下会出现以下异常消息:`Failed to extract the Chromium binaries in the separate process.GZipCompress.exe 已退出,代码为 <exit_code>。
发生此异常时,GZipCompress.exe 的详细输出将写入 DotNetBrowser 日志。检查这些日志通常有助于了解故障的实际原因。
其他 Engine 初始化异常
EngineInitializationException 在初始化 IEngine 实例期间出现错误时抛出。
用户数据目录已被使用
即使在不同的.NET进程中创建这些 IEngine 实例,也不允许创建指向相同用户数据目录的两个或多个 IEngine 实例。Chromium 本身不支持这种用例,它会导致不可预测的行为和 Engine 突然崩溃。
因此,当检测到这种情况时,将抛出 EngineInitializationException 并显示以下消息:The user data directory is already in use: <path>
有几种可能的解决方案:
使用同一个
IEngine创建多个IBrowser实例:C#VBIEngine 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()使用不同的用户数据目录创建单独的
IEngine实例:C#VBIEngine 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实例,任何由此 Engine 创建的IBrowser实例或与这些实例相关的任何其他对象。 - 在 Chromium 崩溃之后,尝试使用
IEngine实例,任何由此 Engine 创建的IBrowser实例或与这些实例相关的任何其他对象。
ConnectionClosedException
ConnectionClosedException 在与 Chromium engine 的连接关闭时抛出。此异常通常与 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 中的 Invoke 和 BeginInvoke 或 WPF 中的 Dispatcher.Invoke 和Dispatcher.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")