I have TEdgeBrowser
and I am using ExecuteScript
method from the DefaultInterface
like this:
EdgeBrowser1->DefaultInterface->ExecuteScript()
ExecuteScript
, when called this way takes 2 parameters:
System::WideChar * javaScript
and const _di_ICoreWebView2ExecuteScriptCompletedHandler handler
What I want to write is a lambda function (using clang compiler) which gets executed (and destroyed) after a script is executed...
Something like:
EdgeBrowser1->DefaultInterface->(L"alert('Hello World!')",
_di_ICoreWebView2ExecuteScriptCompletedHandler(
new TCppInterfacedObject<TCoreWebView2ExecuteScriptCompletedHandler>(
[](HRESULT errorCode, PCWSTR resultObjectAsJson) -> HRESULT {
if (FAILED(errorCode))
{
ShowMessage("Failed to execute script");
return errorCode;
}
ShowMessage("Script executed successfully");
return S_OK;
})));
However, TCoreWebView2ExecuteScriptCompletedHandler
doesn't seem to be defined? What do I need to insert there to make it work?
EDIT:
A few issues I encountered with otherwise excellent reply by @RemyLebeau
WebView2.hpp
the second parameter of Invoke
is defined as System::WideChar *
so I couldn't use LPCWSTR
as it reported that the virtual method Invoke
is unimplemented. Also there wasn't a __stdcall
. Corrected by using instead:HRESULT __stdcall Invoke(HRESULT errorCode, System::WideChar* resultObjectAsJson)
is the use of INTFOBJECT_IMPL_IUNKNOWN
really necessary since the TCppInterfacedObject documentation describes that it already implements IUnknown
methods like QueryInterface
, AddRef
, and Release
? When commented out, there is no warning that those methods are unimplemented, like when it is when I "manually" implement the ICoreWebView2ExecuteScriptCompletedHandler
Is it necessary to Release()
the object before it returns S_OK? I know that there is reference counting involved but does the ExecuteScript
call then does that? Basically, after executing the lambda or instance of ICoreWebView2ExecuteScriptCompletedHandler
, it is no longer needed to be in memory.
The TEdgeBrowser::DefaultInterface
property is the raw ICoreWebView2*
interface pointer provided by Microsoft, just wrapped in the DelphiInterface
class (that is what the _di_
prefix refers to). Microsoft has no concept of _di_
wrapper types.
The 2nd parameter of ICoreWebView2::ExecuteScript()
expects a pointer to an object that implements the ICoreWebView2ExecuteScriptCompletedHandler
interface. You can't pass in a lambda where an interface is expected. So, you need to write a class that implements ICoreWebView2ExecuteScriptCompletedHandler
, and then pass an object instance of that class to ExecuteScript()
.
The way you are trying to use TCppInterfacedObject
for that purpose is not correct. Try this instead:
class TCoreWebView2ExecuteScriptCompletedHandler : public TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler>
{
public:
HRESULT __stdcall Invoke(HRESULT errorCode, WideChar* resultObjectAsJson)
{
if (FAILED(errorCode))
{
ShowMessage("Failed to execute script");
return errorCode;
}
ShowMessage("Script executed successfully");
return S_OK;
}
};
EdgeBrowser1->DefaultInterface->ExecuteScript(
L"alert('Hello World!')",
_di_ICoreWebView2ExecuteScriptCompletedHandler(
new TCoreWebView2ExecuteScriptCompletedHandler()
)
);
If you really want to use a lambda, then you would need something more like this instead:
#include <functional>
class TCoreWebView2ExecuteScriptCompletedHandler : public TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler>
{
public:
using funcType = std::function<HRESULT(HRESULT, LPCWSTR)>;
TCoreWebView2ExecuteScriptCompletedHandler(funcType func) : m_func(func) {}
HRESULT __stdcall Invoke(HRESULT errorCode, WideChar* resultObjectAsJson)
{
return m_func(errorCode, resultObjectAsJson);
}
private:
funcType m_func;
};
EdgeBrowser1->DefaultInterface->ExecuteScript(
L"alert('Hello World!')",
_di_ICoreWebView2ExecuteScriptCompletedHandler(
new TCoreWebView2ExecuteScriptCompletedHandler(
[](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT
{
if (FAILED(errorCode))
{
ShowMessage("Failed to execute script");
return errorCode;
}
ShowMessage("Script executed successfully");
return S_OK;
}
}
)
);
Alternatively:
template<typename FuncType>
class TCoreWebView2ExecuteScriptCompletedHandler : public TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler>
{
public:
TCoreWebView2ExecuteScriptCompletedHandler(FuncType func) : m_func(func) {}
HRESULT __stdcall Invoke(HRESULT errorCode, WideChar* resultObjectAsJson)
{
return m_func(errorCode, resultObjectAsJson);
}
private:
FuncType m_func;
};
auto func = [](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT
{
if (FAILED(errorCode))
{
ShowMessage("Failed to execute script");
return errorCode;
}
ShowMessage("Script executed successfully");
return S_OK;
};
EdgeBrowser1->DefaultInterface->ExecuteScript(
L"alert('Hello World!')",
_di_ICoreWebView2ExecuteScriptCompletedHandler(
new TCoreWebView2ExecuteScriptCompletedHandler<decltype(func)>(func)
)
);
And no, you should not call Release()
from inside the handler itself. ExecuteScript()
will AddRef()
the handler that you pass in if needed, and then Release()
it when done using it. You should only AddRef()
/Release()
your own references to the handler.