我正在使用CEFSharp开发一个项目,它基本上是Chromium Embedded Framework的互操作包装器。该应用程序的目的是使用Chrome引擎的内置转换器加载许多URL并将网页保存为PDF文件。
有时,我尝试加载的其中一个网址会导致问题,而Chromium Web Browser实例将不再有效。由于我不知道API中的任何重新初始化方法,我只需处理当前实例并创建一个新实例。下面的代码说明了我是如何做到的。
代码似乎工作得很好,但有时甚至重新初始化并不能解决我的问题。我想知道我是否做了一些在我的例子中不合适的事情。任何建议都会有用。
另外几个注意事项:在Windows 7计算机上运行它(不要问,我别无选择),我必须将其编译为x86 32位应用程序,因为我正在利用一些较旧的COM互操作组件在应用程序中(不,我不能编译为x64),我使用最新的Nuget版本63.0.2,并不完全确定哪个版本的CEF正在使用。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CefSharp;
using CefSharp.OffScreen;
using CEFNotes.Domain;
using log4net;
namespace CEFNotes.Browser
{
public sealed class WebBrowser : IDisposable
{
private static WebBrowser _webBrowser;
private readonly ILog _log = LogManager.GetLogger(typeof(WebBrowser));
private readonly Dictionary<string, bool> _badHosts = new Dictionary<string, bool>();
private readonly PdfPrintSettings _printSettings = new PdfPrintSettings
{
HeaderFooterEnabled = false,
HeaderFooterTitle = string.Empty, //Constants.Library,
HeaderFooterUrl = "",
PageHeight = 0,
PageWidth = 0,
Landscape = "Landscape".Equals(Constants.Orientation),
MarginType = CefPdfPrintMarginType.Default,
MarginBottom = 0,
MarginLeft = 0,
MarginRight = 0,
MarginTop = 0,
SelectionOnly = false,
BackgroundsEnabled = true
};
private ChromiumWebBrowser _browser;
private bool _disposed;
private WebBrowser()
{
_browser = new ChromiumWebBrowser(string.Empty, new BrowserSettings {WindowlessFrameRate = 1})
{
RequestHandler = new BrowserRequestHandler()
};
var result = Task.Run(async () => await BrowserInitializedAsync()).Result;
if (!result) throw new ApplicationException("Unable to initialize browser.");
}
public static WebBrowser Instance => _webBrowser ?? (_webBrowser = new WebBrowser());
public async Task<string> GetHtml()
{
return await _browser.GetSourceAsync();
}
private Task<bool> BrowserInitializedAsync()
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler handler = null;
handler = (sender, args) =>
{
_browser.BrowserInitialized -= handler;
tcs.TrySetResult(true);
};
if (!_browser.IsBrowserInitialized)
_browser.BrowserInitialized += handler;
else
tcs.TrySetResult(true);
return tcs.Task;
}
public bool Reinitialize()
{
try
{
_browser.Dispose();
_browser = null;
_browser = new ChromiumWebBrowser(string.Empty, new BrowserSettings {WindowlessFrameRate = 1})
{
RequestHandler = new BrowserRequestHandler()
};
return Task.Run(async () => await BrowserInitializedAsync()).Result;
}
catch (Exception ex)
{
_log.Error("Unable to reinitialize", ex);
}
return false;
}
/// <summary>
/// Retrieve the HTML document for the specified URL
/// </summary>
/// <param name="address">The URL for the specific document being retrieved.</param>
/// <returns>Returns a Task with bool value associated with it.</returns>
/// <remarks>
/// https://github.com/cefsharp/CefSharp/issues/1415
/// </remarks>
public Task<bool> LoadPageAsync(string address = null)
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
const int timeout = 20000; // 20 seconds
var timer = new Timer(obj =>
{
try
{
if (!tcs.Task.IsCompleted) tcs.TrySetResult(false);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
, null, timeout, Timeout.Infinite);
var timer1 = timer;
tcs.Task.ContinueWith(t =>
{
//Console.WriteLine("Dispose Continuation ran value is " + t.Status.ToString());
timer1.Dispose();
}
);
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
//Wait for while page to finish loading not just the first frame
if (!args.IsLoading)
{
if (handler != null) _browser.LoadingStateChanged -= handler;
tcs.TrySetResult(true);
}
};
_browser.LoadingStateChanged += handler;
if (!string.IsNullOrEmpty(address)) _browser.Load(address);
return tcs.Task;
}
public Task<bool> PrintToPdfAsync(string path)
{
var browser = _browser.GetBrowser();
var host = browser.GetHost();
var callback = new PrintToPdfCallbackAsync();
host.PrintToPdf(path, _printSettings, callback);
return callback.Task;
}
public void BadHostCallback(string host)
{
if (!_badHosts.ContainsKey(host)) _badHosts.Add(host, true);
}
public bool IsBadHost(string host)
{
return _badHosts.ContainsKey(host);
}
#region IDisposable
~WebBrowser()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) _browser.Dispose();
_disposed = true;
}
#endregion
}
}