还有另外一种方式完成C++和JS交互的方式
WebView2允许把C++对象映射到JavaScript代码中。JavaScript代码拿到C++对象之后,就可以调用C++对象提供的方法了。
首先在工程中创建一个idl文件:
在Windows应用程序开发中,idl文件(Interface Definition Language 文件)主要用于定义跨进程或跨网络的组件间通信接口。
这些接口定义允许不同编程语言编写的组件相互通信,是实现组件对象模型(COM)、远程过程调用(RPC)和对象管理组(OMG)的CORBA等技术的关键元素。
接下来,我们编写这个文件的代码:
import "oaidl.idl";
import "ocidl.idl";
[uuid(BE3AE34D-5FCC-405E-BD52-1CDB06C28D5A), version(0.1)]
library HostLibrary
{
//! [AddHostObjectInterface]
[uuid(C7DEA087-CA5E-4CD7-A1BC-E62D741E3605), object, local]
interface IHost : IUnknown
{
HRESULT CloseWindow();
};
//! [AddHostObjectInterface]
[uuid(637abc45-11f7-4dde-84b4-317d62a638d6)]
coclass Host
{
[default] interface IHost;
interface IDispatch;
};
}
接着编译以下这个文件
编译完成后,会生成两个代码文件
生成的代码文件我们不用管,我们要做的是实现这个接口的类:HostImpl
下面是HostImpl.h的代码
#pragma once
#include <windows.h>
#include <wil/com.h>
#include <wil/resource.h>
#include <wil/result.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <wrl.h>
#include <functional>
#include <map>
#include <string>
#include <wrl/client.h>
#include "Host_h.h"
class Win;
class Host : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IHost, IDispatch>
{
public:
Host(Win* win);
STDMETHODIMP CloseWindow() override;
STDMETHODIMP GetTypeInfoCount(UINT* pctinfo) override;
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override;
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) override;
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override;
private:
Win* win;
wil::com_ptr<ITypeLib> m_typeLib;
};
下面是HostImpl.cpp的代码:
#include "HostImpl.h"
#include "Win.h"
Host::Host(Win* win):win{win}
{
}
STDMETHODIMP Host::CloseWindow()
{
PostMessage(win->hwnd, WM_CLOSE, 0, 0);
return S_OK;
}
STDMETHODIMP Host::GetTypeInfoCount(UINT* pctinfo)
{
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP Host::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
if (0 != iTInfo)
{
return TYPE_E_ELEMENTNOTFOUND;
}
if (!m_typeLib)
{
RETURN_IF_FAILED(LoadTypeLib(L"WebView2JS.tlb", &m_typeLib));
}
return m_typeLib->GetTypeInfoOfGuid(__uuidof(IHost), ppTInfo);
}
STDMETHODIMP Host::GetIDsOfNames(
REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
wil::com_ptr<ITypeInfo> typeInfo;
RETURN_IF_FAILED(GetTypeInfo(0, lcid, &typeInfo));
return typeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
}
STDMETHODIMP Host::Invoke(
DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
wil::com_ptr<ITypeInfo> typeInfo;
RETURN_IF_FAILED(GetTypeInfo(0, lcid, &typeInfo));
return typeInfo->Invoke(
this, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}
我们这个对象公开了一个CloseWindow方法。
其他方法都是JS访问C++对象必须的,GetTypeInfo方法加载了一个tlb文件
编译运行程序,你会发现输出目录下就有这个tlb文件,这个文件的名字别搞错了。
现在需要把这个对象交给WebView2控件,代码如下:
// #include "HostImpl.h"
hostObj = Microsoft::WRL::Make<Host>(this);
VARIANT remoteObjectAsVariant = {};
hostObj.query_to<IDispatch>(&remoteObjectAsVariant.pdispVal);
remoteObjectAsVariant.vt = VT_DISPATCH;
webview->AddHostObjectToScript(L"host", &remoteObjectAsVariant);
remoteObjectAsVariant.pdispVal->Release();
这里的this就是我们前面介绍的Win对象。
现在我们写JavaScript代码,来调用HostImpl对象的这个CloseWindow
let closeBtn = document.getElementById("closeBtn");
closeBtn.addEventListener("mousedown", (e) => {
e.preventDefault();
e.stopPropagation();
chrome.webview.hostObjects.sync.host.CloseWindow();
})
现在点击这个closeBtn就可以调用C++的CloseWindow方法了。