Sergej Loos
Sergej Loos

Reputation: 312

How to get sync return from ExecuteScriptAsync in WebView2

.NET, WinForms.

The calls are triggered from the UI thread (buttons - clicks). The returns from ExecuteScriptAsync should continue to be processed synchronously, i.e. they should be synchronized again with the call context. I fail here.

I tried for example:

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)");
    }

And that doesn't work either (see error):

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;
    }

I am bidding for a code sample.

Upvotes: 6

Views: 20738

Answers (5)

user14967521
user14967521

Reputation:

For Anyone working with WebView2 under WinForms and looking for Safe, Simple and Anti-Blocking Solution these lines of codes may seem logical:

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 is user defined and assigned before Async process start.

To call this method from main method:

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();
}

Now call these helper methods from this method:

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();
}

Upvotes: 2

Nikola
Nikola

Reputation: 125

I used my own solution which worked for me, but I don't know if it can cause any problems in the future (if someone knows, please add comment). It waits 2 seconds for script to execute before jumping out:

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
}

Upvotes: 2

Koxo
Koxo

Reputation: 517

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

Upvotes: -2

Reza Aghaei
Reza Aghaei

Reputation: 125197

To get the result from ExecuteScriptAsync use await operator, like this:

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);
}

Note: For those who want to use WebView2, you need to have WebView2 Runtime and Microsoft Edge Chromium installed on your machine. You also need to install WebView2 NuGet package in your project.

Upvotes: 9

Sergej Loos
Sergej Loos

Reputation: 312

I will probably have to use it that way. This is the best solution I could find:

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()
        );

    }

Upvotes: 5

Related Questions