在 MFC 的 **CWebBrowser** 控件中,通过使用 COM 接口,可以实现 JavaScript 调用 C++ 的功能。以下是详细步骤:
---
### **实现原理**
1. **注册 ActiveX 接口:**
将 C++ 的方法通过 COM 的方式暴露给 JavaScript 使用。
2. **网页调用 COM 方法:**
JavaScript 可以通过 `window.external` 对象调用暴露的接口方法。
3. **通信逻辑:**
- C++ 定义接口并实现方法。
- JavaScript 调用这些方法并接收返回值。
---
### **实现步骤**
#### **1. 定义 C++ COM 接口**
创建一个继承自 `IDispatch` 的 COM 类,用于定义 C++ 的方法。通过 `IDispatch`,可以暴露方法给 JavaScript。
```cpp
// MyBrowserInterface.h
#pragma once
#include // 包含 IDispatch
class CMyBrowserInterface : public IDispatch
{
public:
CMyBrowserInterface();
virtual ~CMyBrowserInterface();
// IUnknown 接口方法
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IDispatch 接口方法
STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
// 自定义方法
HRESULT ShowMessage(BSTR message);
HRESULT AddNumbers(int a, int b, int* result);
private:
ULONG m_refCount;
};
```
实现 `CMyBrowserInterface.cpp`:
```cpp
#include "MyBrowserInterface.h"
#include
CMyBrowserInterface::CMyBrowserInterface() : m_refCount(1) {}
CMyBrowserInterface::~CMyBrowserInterface() {}
// IUnknown 方法
STDMETHODIMP CMyBrowserInterface::QueryInterface(REFIID riid, void** ppvObject)
{
if (riid == IID_IUnknown || riid == IID_IDispatch)
{
*ppvObject = static_cast(this);
AddRef();
return S_OK;
}
*ppvObject = nullptr;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CMyBrowserInterface::AddRef()
{
return InterlockedIncrement(&m_refCount);
}
STDMETHODIMP_(ULONG) CMyBrowserInterface::Release()
{
ULONG count = InterlockedDecrement(&m_refCount);
if (count == 0)
delete this;
return count;
}
// IDispatch 方法
STDMETHODIMP CMyBrowserInterface::GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; }
STDMETHODIMP CMyBrowserInterface::GetTypeInfo(UINT, LCID, ITypeInfo**) { return E_NOTIMPL; }
STDMETHODIMP CMyBrowserInterface::GetIDsOfNames(REFIID, LPOLESTR* rgszNames, UINT cNames, LCID, DISPID* rgDispId)
{
if (wcscmp(rgszNames[0], L"ShowMessage") == 0)
{
*rgDispId = 1;
return S_OK;
}
else if (wcscmp(rgszNames[0], L"AddNumbers") == 0)
{
*rgDispId = 2;
return S_OK;
}
return DISP_E_UNKNOWNNAME;
}
STDMETHODIMP CMyBrowserInterface::Invoke(DISPID dispIdMember, REFIID, LCID, WORD, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO*, UINT*)
{
if (dispIdMember == 1) // ShowMessage
{
if (pDispParams->cArgs == 1 && pDispParams->rgvarg[0].vt == VT_BSTR)
{
CString message(pDispParams->rgvarg[0].bstrVal);
AfxMessageBox(message);
return S_OK;
}
return DISP_E_BADPARAMCOUNT;
}
else if (dispIdMember == 2) // AddNumbers
{
if (pDispParams->cArgs == 2 &&
pDispParams->rgvarg[0].vt == VT_I4 &&
pDispParams->rgvarg[1].vt == VT_I4)
{
int a = pDispParams->rgvarg[1].intVal;
int b = pDispParams->rgvarg[0].intVal;
int result = a + b;
if (pVarResult)
{
pVarResult->vt = VT_I4;
pVarResult->intVal = result;
}
return S_OK;
}
return DISP_E_BADPARAMCOUNT;
}
return DISP_E_MEMBERNOTFOUND;
}
```
---
#### **2. 将接口绑定到 WebBrowser**
将 `CMyBrowserInterface` 绑定到 `CWebBrowser` 控件,使其暴露给 JavaScript。
```cpp
CComPtr m_spBrowserInterface;
// 初始化接口并绑定
m_spBrowserInterface = new CMyBrowserInterface();
CComQIPtr spAmbient(m_browser.GetControlUnknown());
if (spAmbient)
{
spAmbient->PutExternalDispatch(m_spBrowserInterface);
}
```
---
#### **3. JavaScript 调用 C++ 方法**
在 HTML 文件中,通过 `window.external` 访问 `CMyBrowserInterface` 的方法:
```html
JavaScript to C++
```
---
### **注意事项**
1. **安全性:** 确保暴露的接口不会被恶意 JavaScript 滥用。
2. **调试:** 使用浏览器调试工具检查 JavaScript 调用的返回值和错误信息。
3. **兼容性:** CWebBrowser 控件使用 IE 引擎,确保脚本支持的功能与目标 IE 版本一致。
0 Comments latest
No comments.