无法使CefSharp同步加载

4
我有一个离屏的ChromiumWebBrowser,我用它来运行一些JS并获取结果。虽然Load方法是同步的,但在FrameLoadEnd事件被触发之前我无法运行JS代码,这意味着对于我的目的而言,Load方法是一个以FrameLoadEnd事件结束的异步方法。
为了使我的代码更清晰,我尝试创建一个扩展方法,允许我使用await等待页面加载,而不是注册事件。但是当我使用TaskCompletionSource与此方法一起使用时,应该在页面加载后运行的JavaScript未加载,但是使用AutoResetEvent并等待它时,代码确实可以正常工作。
以下代码无效:
public static Task LoadPageAsync(this IWebBrowser browser, string address)
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    browser.FrameLoadEnd += (sender, args) =>
    {
        if (args.IsMainFrame)
        {
            tcs.TrySetResult(true);
        }
    };
    browser.Load(address);
    return tcs.Task;
}

并且这样做:

public static AutoResetEvent LoadPageAsync(this IWebBrowser browser, string address)
{
    AutoResetEvent are = new AutoResetEvent(false);
    browser.FrameLoadEnd += (sender, args) =>
    {
        if (args.IsMainFrame)
        {
            are.Set();
        }
    };
    browser.Load(address);
    return are;
}

这是调用代码:
await _browser.LoadPageAsync("Some web address");
LoadScripts();

DoJsThing();
GetJsData();

它们做的事情相同,但我觉得当返回任务而不是返回AutoResetEvent时,函数的意图更加清晰。

为什么我不能使用TaskCompletionSource指示我已经完成了?我做错了什么吗?


Load是同步的,但在主框架加载之前它不会执行任何有意义的操作(或者至少不会执行我关心的操作)。这意味着在常规代码中,我会调用Load,然后我的程序的其余部分将在FrameLoadEnd的事件代码中继续执行,这会导致代码跳转的奇怪现象。 - Ziv
事件应该在页面加载后立即发生吗? - Yuval Itzchakov
图书馆在页面加载后会触发该事件,是的。 - Ziv
await 只会阻塞而不会返回吗? - amaitland
你能否fork MinimalExample来演示你的确切问题?https://github.com/cefsharp/CefSharp.MinimalExample - amaitland
显示剩余3条评论
1个回答

6
public static Task LoadPageAsync(IWebBrowser browser, string address = null)
{
    var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

    EventHandler<LoadingStateChangedEventArgs> handler = null;
    handler = (sender, args) =>
    {
        //Wait for while page to finish loading not just the first frame
        if (!args.IsLoading)
        {
            browser.LoadingStateChanged -= handler;
            //Important that the continuation runs async using TaskCreationOptions.RunContinuationsAsynchronously
            tcs.TrySetResult(true);
        }
    };

    browser.LoadingStateChanged += handler;

    if (!string.IsNullOrEmpty(address))
    {
        browser.Load(address);
    }
    return tcs.Task;
}

请注意,我使用了LoadingStateChanged,因为它是整个页面加载完成的更好指示器。


谢谢,这个可行。我尝试过复制最小的示例,但无法重现问题。 - Ziv
@amaitland,我在IWebBrowser中没有看到NavStateChanged事件属性,我应该在哪里找到它?使用的是CefSharp 47.0.2。 - Kapé
2
它已被重命名为LoadingStateChanged,请参见https://gist.github.com/amaitland/9d8897067bdff5b999a1#file-program-cs-L60以获取示例。 - amaitland
这个例子适用于离屏解决方案。在显示窗体本身之前,尝试完全加载 WinForms 应用程序中的页面会使程序员的生活变成一场噩梦。 - user2440074
代码示例已经替换了链接,这是从 https://github.com/cefsharp/CefSharp/blob/cefsharp/79/CefSharp.OffScreen.Example/Program.cs#L117 找到的一个工作示例的摘录。 - amaitland

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