How to wait for completion of a call to ExecuteScript on a WebView2 interface in a C++ COM STA

2.1k views Asked by At

I have an COM STA, that hosts an ICoreWebView2.

I try to get the complete HTML block and I found a documentation to achieve this with a script. Here my code:

    hr = m_spWebView->ExecuteScript(L"document.body.outerHTML",
        Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
            [&val](HRESULT hr, LPCWSTR result) -> HRESULT 
            {
                if (SUCCEEDED(hr))
                    val = result;
                return S_OK;
            }
        ).Get()
    );

This code works, but it is executed asynchronous. So it takes some time to get the result. In fact I can see that the result arrives when the message pump is executed the next time (as I expect for an STA).

In C# I would use an await to wait for the completion. But using C++ there is nothing like this. Using an event wouldn't work, because I have an STA I would block the thread and the answer will never arrive.

Is there any way to call a function that waits for the completion in C++? Or another help would be to use ExecuteScript synchron.

2

There are 2 answers

1
David Risney On

If you have an STA then rather than explicitly waiting for completion, you can start your message loop or return back and allow your message loop to continue processing messages.

If you need to block execution and process messages without returning back to your message loop, you can try using CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES to ensure you process window messages which is necessary for WebView2 callbacks to execute. However, this can open up your app to reentrancy or other synchronization issues and depending on how the rest of your app deals with this, could be a problem.

Generally the better solution is to return back to your message loop.

1
ChrCury78 On

You have to implement an IDispatch interface (COM stuf), and add it with:

m_spWebView->AddHostObjectToScript(L"host", &disp);

Then you let JavaScript call it when the documment is loaded.

struct CDispatch : IDispatch
{
...
} cdisp;

You will get the HTML as a parameter in a Invoke call. Don't need to worry about the other methods, just confirm for IID_IDispatch in QueryInterface and watch for the Invoke.

 window.chrome.webview.hostObjects.host(document.body.outerHTML);

I tried here and managed to do what you are willing to do.