如何编写TEdgeBrowser ExecuteScript执行后的Lambda函数及相关疑问
问题描述
我使用TEdgeBrowser组件,通过DefaultInterface调用ExecuteScript方法,代码框架如下:
EdgeBrowser1->DefaultInterface->ExecuteScript()
该方法需要传入两个参数:System::WideChar * javaScript 和 const _di_ICoreWebView2ExecuteScriptCompletedHandler handler。
我想用clang编译器写一个Lambda函数作为回调,执行完成后自动销毁,尝试了以下代码:
EdgeBrowser1->DefaultInterface->ExecuteScript(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; })));
但发现TCoreWebView2ExecuteScriptCompletedHandler未定义,请问该如何修改才能正常运行?
另外还有三个疑问:
- 在
WebView2.hpp中,Invoke方法的第二个参数定义为System::WideChar *,用LPCWSTR会提示虚方法Invoke未实现,必须添加__stdcall,我已经修正为:
HRESULT __stdcall Invoke(HRESULT errorCode, System::WideChar* resultObjectAsJson)
这样处理是否正确?
2. 根据TCppInterfacedObject的文档,它已经实现了QueryInterface、AddRef、Release等IUnknown方法,那还需要使用INTFOBJECT_IMPL_IUNKNOWN宏吗?注释掉这个宏后没有出现方法未实现的警告,但手动实现ICoreWebView2ExecuteScriptCompletedHandler时会出现警告。
3. 在返回S_OK前需要调用Release()吗?已知存在引用计数机制,ExecuteScript会处理引用计数吗?我希望这个Lambda或ICoreWebView2ExecuteScriptCompletedHandler实例执行后不再占用内存。
核心问题解决:TCoreWebView2ExecuteScriptCompletedHandler未定义的处理
你需要自行实现一个继承自ICoreWebView2ExecuteScriptCompletedHandler的适配类,用来桥接Lambda和WebView2的回调接口。以下是两种可行的写法:
写法一:自定义回调处理类
// 继承TCppInterfacedObject,自动获得IUnknown的实现 class TCoreWebView2ExecuteScriptHandler : public TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler> { public: using TCallback = std::function<HRESULT(HRESULT, PCWSTR)>; TCallback FCallback; explicit TCoreWebView2ExecuteScriptHandler(TCallback&& Callback) : FCallback(std::move(Callback)) {} // 严格匹配接口定义的Invoke方法签名 HRESULT __stdcall Invoke(HRESULT errorCode, System::WideChar* resultObjectAsJson) override { if (FCallback) { return FCallback(errorCode, resultObjectAsJson); } return E_FAIL; } }; // 调用ExecuteScript的代码 EdgeBrowser1->DefaultInterface->ExecuteScript(L"alert('Hello World!')", _di_ICoreWebView2ExecuteScriptCompletedHandler( new TCoreWebView2ExecuteScriptHandler( [](HRESULT errorCode, PCWSTR resultObjectAsJson) -> HRESULT { if (FAILED(errorCode)) { ShowMessage("Failed to execute script"); return errorCode; } ShowMessage("Script executed successfully"); return S_OK; })));
写法二:直接适配Lambda到接口
利用TCppInterfacedObject的Lambda适配能力,直接实现接口的Invoke方法:
EdgeBrowser1->DefaultInterface->ExecuteScript(L"alert('Hello World!')", _di_ICoreWebView2ExecuteScriptCompletedHandler( new TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler>( // 定义Lambda回调逻辑 [](HRESULT errorCode, System::WideChar* result) -> HRESULT { if (FAILED(errorCode)) { ShowMessage("Failed to execute script"); return errorCode; } ShowMessage("Script executed successfully"); return S_OK; }, // 桥接接口Invoke方法到Lambda [](ICoreWebView2ExecuteScriptCompletedHandler* iface, HRESULT errorCode, System::WideChar* result) -> HRESULT { auto lambdaHolder = static_cast<TCppInterfacedObject<ICoreWebView2ExecuteScriptCompletedHandler>*>(iface); auto callback = lambdaHolder->GetLambda(); return callback(errorCode, result); })));
关键注意点:必须保证Invoke方法的参数类型、顺序和调用约定(__stdcall)完全匹配ICoreWebView2ExecuteScriptCompletedHandler的定义。
疑问解答
关于
Invoke方法的修正
你的修正完全正确。WebView2.hpp中定义的Invoke方法签名是固定的,必须严格匹配HRESULT __stdcall Invoke(HRESULT, System::WideChar*),否则编译器会判定你未实现接口的纯虚函数,导致编译失败。是否需要
INTFOBJECT_IMPL_IUNKNOWN宏
不需要。TCppInterfacedObject已经内置了IUnknown核心方法(QueryInterface、AddRef、Release)的实现,继承它就无需再使用该宏。而手动实现ICoreWebView2ExecuteScriptCompletedHandler时,因为没有继承TCppInterfacedObject,必须通过INTFOBJECT_IMPL_IUNKNOWN宏生成IUnknown的实现,否则会出现方法未实现的警告。是否需要手动调用
Release()
不需要手动调用。ExecuteScript方法会在回调执行完成后自动调用Release(),TCppInterfacedObject的引用计数机制会在计数降为0时自动销毁对象。整个生命周期由WebView2的内部逻辑管理,不会造成内存泄漏。
内容的提问来源于stack exchange,提问作者Coder12345

