如何从WebView2的ExecuteScriptAsync方法中获取同步返回

6

.NET,WinForms。

这些调用是由UI线程触发的(按钮-点击)。ExecuteScriptAsync的返回值应该继续以同步方式进行处理,即它们应该再次与调用上下文同步。我在这里失败了。

我尝试过例如:

private void buttonTest1_Click(object sender, EventArgs e) {
        MessageBox.Show(GetMathResult());
    }

    String GetMathResult() {
        // a) Application freezes
        //var result = webView.ExecuteScriptAsync("Math.sin(Math.PI/2)").GetAwaiter().GetResult();
        //return result;

        // b) return null
        //String result = null;
        //Task task = new Task(async () => { result = await webView.ExecuteScriptAsync("Math.sin(Math.PI/2)"); }); 
        //task.RunSynchronously();
        //return result;

        // c) Excepion: // InvalidCastException: Das COM-Objekt des Typs "System.__ComObject" kann nicht in den Schnittstellentyp "Microsoft.Web.WebView2.Core.Raw.ICoreWebView2Controller" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die COM - Komponente für die Schnittstelle mit der IID "{4D00C0D1-9434-4EB6-8078-8697A560334F}" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte: Schnittstelle nicht unterstützt(Ausnahme von HRESULT: 0x80004002(E_NOINTERFACE)).
        //String result = Task.Run(() => GetMathResultTask()).Result;
        //return result;
    }

    Task<String> GetMathResultTask() {
        return webView.ExecuteScriptAsync("Math.sin(Math.PI/2)");
    }

那样也行不通(看到错误信息):

private void buttonTest3_Click(object sender, EventArgs e) {
        MessageBox.Show(Y());
    }

    String Y() {
        String result = null;
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try {
                result = await webView.ExecuteScriptAsync("Math.sin(Math.PI/2)");
            }
            catch (Exception exc) {
                // !!! {"Das COM-Objekt des Typs \"System.__ComObject\" kann nicht in den Schnittstellentyp \"Microsoft.Web.WebView2.Core.Raw.ICoreWebView2Controller\" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die COM-Komponente für die Schnittstelle mit der IID \"{4D00C0D1-9434-4EB6-8078-8697A560334F}\" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte: Schnittstelle nicht unterstützt (Ausnahme von HRESULT: 0x80004002 (E_NOINTERFACE))."}
                Console.WriteLine(exc.ToString()); 
            }
            finally {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

我正在竞标一份代码示例。


1
像任何其他异步方法一样,使用await。 - Reza Aghaei
我认为我尝试了下面链接中建议的所有方法,但都没有成功: https://dev59.com/G2445IYBdhLWcg3wAFcC - Sergej Loos
更新问题并添加您尝试过的内容以及未能解决的问题。"它不起作用"不是一个问题描述。您不需要复杂的技巧来等待异步方法并获取任何结果。var result=await whatever.ExecuteScriptAsync(....); - Panagiotis Kanavos
await 完成后,您将回到 UI 线程。那么 I fail here. 是什么意思呢?您是否尝试通过 Task.Run 或从另一个线程执行调用? - Panagiotis Kanavos
private async void button1_Click(object sender, EventArgs e) { MessageBox.Show(await GetMathResultTask()); } - Reza Aghaei
显示剩余2条评论
5个回答

9

要从ExecuteScriptAsync中获得结果,请使用await运算符,像这样:

private async void Form1_Load(object sender, EventArgs e)
{
    await this.webView21.EnsureCoreWebView2Async();
}
private async void button1_Click(object sender, EventArgs e)
{
    var result = await webView21.ExecuteScriptAsync("Math.sin(Math.PI/2)");
    MessageBox.Show(result);
}

注意:如果您想使用WebView2,您需要在计算机上安装WebView2 运行时Microsoft Edge Chromium。同时,在您的项目中还需要安装WebView2 NuGet 包


谢谢!我可以做到那个。我失败的地方在于创建一个返回方法。根据他们的示例: async String GetMathResult() { var result = await webView.ExecuteScriptAsync("Math.sin(Math.PI/2)"); return result; } - Sergej Loos
@SergejLoos你输入的代码是可以工作的,只需要将其改为async Task<string>。问题出在哪里?你实际尝试了什么? - Panagiotis Kanavos
我编辑了我的问题。希望现在更易于理解。 - Sergej Loos
@SergejLoos 我在搜索某些内容时偶然发现了这篇帖子,但它没有被采纳的答案。顺便说一下,你也可以接受自己的答案;不过,我认为这个答案比其他解决方案(包括你自己的答案)更好 :) - 无论如何,将一个答案标记为已采纳,可以让未来的读者知道这可能是一个可靠的解决方案。 - Reza Aghaei

4

我可能必须以这种方式使用它。这是我能找到的最佳解决方案:

private void buttonTest3_Click(object sender, EventArgs e) {
        GetMathResult_v3((x) => {
            MessageBox.Show(x);
            // .. 
        });
    }

    void GetMathResult_v3(Action<String> callbackAction) {

        var task = webView.ExecuteScriptAsync("Math.sin(Math.PI/2)");

        task.ContinueWith(
            (x) => {
                String mathResult = x.Result;
                callbackAction(mathResult);
            }
            , TaskScheduler.FromCurrentSynchronizationContext()
        );

    }

2

对于在WinForms下使用WebView2并寻找安全、简单和无阻塞解决方案的任何人来说,以下代码可能看起来很合乎逻辑:

public void WaitAsyncTask(int waitSecond, System.Runtime.CompilerServices.TaskAwaiter activeTask)
{
    double secondDiffer = (DateTime.Now - WaitStartTime).TotalSeconds;
    while (secondDiffer < waitSecond)
    {
        secondDiffer = (DateTime.Now - WaitStartTime).TotalSeconds;
        Application.DoEvents();
        if (activeTask.IsCompleted)
            break;
    }
}

WaitStartTime是在Async进程开始之前由用户定义和分配的。

从主方法调用此方法:

public async Task ExecJavaScript(string userScript)
{
    MacroJava = "";
    var tr = await ((Microsoft.Web.WebView2.WinForms.WebView2)wbSelectedTech).CoreWebView2.ExecuteScriptAsync(userScript);
    MacroJava = System.Text.Json.JsonSerializer.Deserialize<dynamic>(tr);
    MacroJava = MacroJava is null ? "" : MacroJava.ToString();
}

现在从这个方法中调用这些辅助方法:
public string GetelementValue(string elmId)
{
   var aw = ExecJavaScript("document.getElementById('" + elmId + "').innerHTML").GetAwaiter();
   TimeWaitStart = DateTime.Now;
   WaitAsyncTask(2, aw);//for 2 seconds maximum 
   return MacroJava.ToString();
}

2
我使用了自己的解决方案,对我来说有效,但我不知道它是否会在未来引起任何问题(如果有人知道,请添加评论)。它在脚本执行前等待2秒钟再跳出:
int max_count = 200;
var task = webView2.ExecuteScriptAsync(script);
while (!task.IsCompleted && --max_count > 0)
{
    Application.DoEvents();
    System.Threading.Thread.Sleep(10);
}
if (max_count > 0)
{
    result = JsonConvert.DeserializeObject(task.Result)?.ToString() ?? "";
}
else
{
    // Timeout
}

-2
webView.ExecuteScriptAsync("Math.sin(Math.PI/2)").Result;

1
这不起作用。 - Cowas

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