gpt4 book ai didi

c# - 在异步任务中并行使用 WebView2

转载 作者:行者123 更新时间:2023-12-03 13:22:47 30 4
gpt4 key购买 nike

我有简单的默认 Windows 桌面窗体 Form1一键式btn_Go作为测试。
我想运行多个并行的 WebView2 实例并处理渲染页面中的 html 代码。要并行运行 WebView2,我使用 SemaphoreSlim(设置为并行 2)。另一个 SemaphoreSlim 用于等待 WebView2 呈现文档(有一些时间延迟)。

但我的代码落在await webBrowser.EnsureCoreWebView2Async(); . WebView2 中调试器的内部异常实例 webBrowser是:

{"Cannot change thread mode after it is set. (Exception from HRESULT: 0x80010106 (RPC_E_CHANGED_MODE))"} System.Exception {System.Runtime.InteropServices.COMException}

如何并行多次调用 WebView2 并处理所有 url?

完整的演示代码:

using Microsoft.Web.WebView2.WinForms;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebView2Test
{
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }

private void btn_Go_Click(object sender, EventArgs e) { Start(); }

private async void Start()
{
var urls = new List<string>() { "https://www.google.com/search?q=test1", "https://www.google.com/search?q=test2", "https://www.google.com/search?q=test3" };
var tasks = new List<Task>();
var semaphoreSlim = new SemaphoreSlim(2);
foreach (var url in urls)
{
await semaphoreSlim.WaitAsync();
tasks.Add(
Task.Run(async () => {
try { await StartBrowser(url); }
finally { semaphoreSlim.Release(); }
}));
}
await Task.WhenAll(tasks);
}

public async Task<bool> StartBrowser(string url)
{
SemaphoreSlim semaphore = new System.Threading.SemaphoreSlim(0, 1);

System.Timers.Timer wait = new System.Timers.Timer();
wait.Interval = 500;
wait.Elapsed += (s, e) =>
{
semaphore.Release();
};
WebView2 webBrowser = new WebView2();
webBrowser.NavigationCompleted += (s, e) =>
{
if (wait.Enabled) { wait.Stop(); }
wait.Start();
};
await webBrowser.EnsureCoreWebView2Async();
webBrowser.CoreWebView2.Navigate(url);

await semaphore.WaitAsync();
if (wait.Enabled) { wait.Stop(); }

var html = await webBrowser.CoreWebView2.ExecuteScriptAsync("document.documentElement.outerHTML");
return html.Length > 10;
}
}
}

我有WebView2 runtime安装。

-- 测试

准备 WebView2在主线程中并将其发送到子线程中。

我已经尝试创建 WebView2 的列表在 Start来自主线程的方法 var bro = new List<WebView2>() { new WebView2(), new WebView2(), new WebView2() };并将 WebView2 实例发送到 await StartBrowser(bro[index], url); ...但这以同样的错误结束。

最佳答案

您可以尝试用下面的自定义 TaskRunSTA 方法替换代码中的 Task.Run:

public static Task TaskRunSTA(Func<Task> action)
{
var tcs = new TaskCompletionSource<object>(
TaskCreationOptions.RunContinuationsAsynchronously);
var thread = new Thread(() =>
{
Application.Idle += Application_Idle;
Application.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;

async void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
try
{
await action();
tcs.SetResult(null);
}
catch (Exception ex) { tcs.SetException(ex); }
Application.ExitThread();
}
}

此方法启动一个新的 STA 线程,并在该线程内运行专用的应用程序消息循环。作为参数传递的异步委托(delegate)将在此消息循环上运行,并安装了适当的同步上下文。只要您的异步委托(delegate)不包含任何使用 .ConfigureAwait(false) 配置的 await,您的所有代码,包括 WebView2 引发的事件> 组件,应在此线程上运行。


注意: TBH 我不知道是否处理第一次出现的 Application.Idle事件是在消息循环中嵌入自定义代码的最佳方式,但它似乎工作得很好。值得注意的是,此事件与内部类 ThreadContext ( source code ) 附加和分离,并且此类每个线程都有一个专用实例。因此,每个线程都会收到与该线程上运行的消息循环相关联的 Idle 事件。换句话说,不存在接收源自其他不相关消息循环的事件的风险,该消息循环在另一个线程上并发运行。

关于c# - 在异步任务中并行使用 WebView2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66394112/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com