Jump to content
Yaron

WebView2 synchronious calls

Recommended Posts

I am using the microsoft's WebView2 API to embed the chromium based edge browser inside my Delphi 7 application (using Winsoft's WebView2 wrapper component).

 

My problem is that WebView2's ExecuteScript function returns results asynchronously through a call-back function, which is a problem because my app needs to setup a few things based on the javascript function's result and can't proceed until the data is available.

I couldn't find any way in Delphi to process the results synchronously without calling "Application.ProcessMessages" in a loop until the result function is triggered (I can't use "Sleep" because then the result function will never be triggered).

 

Of course calling "Application.ProcessMessages" in a loop is not very desirable, so I'm wondering if there's a better approach?

 

Share this post


Link to post

You should think "event driven" and no "synchronous".

Put all your code after the ExecuteScript call into another function. Then call that function from the callback.

Probably your application will need other changes to become fully event driven without using any "wait loop" hack.

 

 

  • Like 3

Share this post


Link to post

I'm not sure how event driven would help me here, I have to wait until specific data is returned from a javascript function before allowing the code to proceed.

 

Here's a real life example:

1. A user presses "play" in my app to play content.

2. I execute a javascript function to return data on the content (e.g. duration in seconds).

3. I now need to display the a position bar on a timeline to the user, which uses a combination of duration and the current position (which isn't always "0" when starting playback of live feeds).

 

As soon as the user presses "play", I want to block all UI interaction until I get my data back from the javascript function.

Sure, I can artificially block UI interaction by disabling every UI entry point, but that would be a messy workaround when all I need is the javascript function to return the result synchronously.

 

Share this post


Link to post

Sorry, but I'm able to help as I don't use it, but may I start a small not-so-off-topic discussion?

When I first read from your post that winsoft has a WebView2 wrapper which supports old version of Delphi, I was exciting, thought that old Delphi can finally have the "TEdgeBrowser" component introduced in newer Delphi.

Than I realized that in order to use it, the client PC would have to download a 123 MB WebView2 runtime!

 

Oh, in that case, I think I'll keep using our good old Cef4Delphi...

 

I'm puzzled, it seems that TEdgeBrowser also request that 120+ MB WebView2 run-time. I mean, why bother? Why not just use cef4delphi which provides you much more power and flexibility? This is a genuine question.

Share this post


Link to post
24 minutes ago, Yaron said:

As soon as the user presses "play", I want to block all UI interaction until I get my data back from the javascript function.

Why would you want to do that? Is a progress bar that vital?

Why not just

  1. Go fetch progress
  2. On Result: paint progress bar

Which is what @FPiette has outlined with moving the code that works with the JavaScript result into a seperate function and calling that one from your callback.

Share this post


Link to post

 

Quote

I have to wait until specific data is returned from a JavaScript function before allowing the code to proceed.

 

 

You don't wait. As I said, put the code to be executed after JavaScript finished execution in a function and call that function from the callback. A call back function in Delphi terminology is an event handler..

 

Quote

 

Here's a real life example:

1. A user presses "play" in my app to play content.

2. I execute a JavaScript function to return data on the content (e.g. duration in seconds).

3. I now need to display the a position bar on a timeline to the user, which uses a combination of duration and the current position (which isn't always "0" when starting playback of live feeds).

 

Your app doesn't wait that the user press the "play" button. You get an event when the button is pressed and from there, you start the task you have to do, that is execute JavaScript.

Then, you don't wait for the JavaScript execution, you just return. The code to be executed (A position bar on the timeline) once JavaScript is done is called from the WebView2 callback (That is an event).

 

Quote

As soon as the user presses "play", I want to block all UI interaction until I get my data back from the JavaScript function.

From the "play" button click event, you disable the button and you re-enable it once you get the JavaScript done.

 

  • Like 1

Share this post


Link to post

@FPiette My application has over 100 keyboard macros, many dynamically created UI elements (skinned buttons) and even a TCP/IP control API (used for remote control) that can trigger events that should not be activated while a new media is in the process of loading, I can't simply disable one button, I have to disable 100's of elements and several event triggers that may execute unwanted functionality while waiting for the javascript callback event to trigger. Sure, I can do that, but it would take a lot more work than just having WebView2 return a result synchronously like TWebBrowser is able to do.

 

@Edwin Yip I considered CEF4Delphi, but from my initial investigation, there were several show-stoppers, including the possible illegality of including audio/video codecs required by YouTube in the compiled binary, something I'm not willing to do. With regards to the 120+ MB WebView2 download, it won't be a thing in Windows 11 as WebView2 comes pre-installed on Win11.  For Win10, I offer my users a quick setup option to download and install the evergreen version of WebView2 without much hassle.

 

  • Like 1

Share this post


Link to post
2 hours ago, Yaron said:

I have to disable 100's of elements and several event triggers that may execute unwanted functionality while waiting for the javascript callback event to trigger.

One possibility I see is to open a modal form just after the call to ExecuteScript and have that form closed by the callback. The modal form could be empty or even so small that the user won't notice it or it can be a place for a progress bar, a cancel button or just a "please wait" message.

Share this post


Link to post

The modal form can also be off-screen.
Form.Height := 1;
Form.Widht := 1;
Form.Top := -100;
Form.Left := -100;
I even use it. The reason is its unwanted rendering and flickering.

Share this post


Link to post

You also can run custom message loop that will process WV's messages and stash/drop/whatever all others.

Or, just switch the main window off with EnableWindow (that's how modality works in fact).

Share this post


Link to post

This is a bit of an old topic but since I found it via a google search people in the future will likely see it too, so here are my two cents:

 

Using a modal form is almost the same as having a loop with Application.ProcessMessages in it. The .ShowModal function literally contains this a repeat-until loop with Application.ProcessMessages inside of it.

 

BUT:

If you don't use a modal form then Application.ProcessMessages will execute things like clicking on buttons on other forms than the modal form which isn't desired while you wait for the javascript response. Meanwhile .ShowModal blocks the use of all other forms except the form it was called from.

 

So either use a modal form as the easy way out or add a boolean that is set while waiting for the response in a loop with Application.ProcessMessages. All event functions for gui interaction should check that and exit if it is true. Problem solved.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×