gpt4 book ai didi

javascript - 从运行在 Web 浏览器控件中的 JavaScript 脚本调用 C++ 函数

转载 作者:IT老高 更新时间:2023-10-28 23:21:05 24 4
gpt4 key购买 nike

我在我的 c++ 应用程序中嵌入了一个 Web 浏览器控件。我希望在 Web 浏览器控件中运行的 javascript 能够调用 c++ 函数/方法。

我发现了三种方法可以做到这一点:

  1. 实现一个充当中间人的 ActiveX 组件。 (这里的实现细节:http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
  2. 使用 window.external。 (也在上面的链接中讨论过,但没有提供实现)
  3. 向窗口对象添加自定义对象

我想选择第三个选项,但我还没有找到任何可行的例子来说明如何做到这一点。有人可以告诉我怎么做,或者链接到网上某个地方的工作示例。

我发现的最接近示例的是 Igor Tandetnik 在 a thread in the webbrowser_ctl news group 中的第一个回复.但恐怕我需要更多的帮助。

我正在嵌入一个 IWebBrowser2 控件,但我没有使用 MFC、ATL 或 WTL。

编辑:

按照我之前链接的线程中 Igor 给出的伪代码,以及在 codeproject 文章“Creating JavaScript arrays and other objects from C++”中找到的代码,我已经生成了一些代码。

void WebForm::AddCustomObject(IDispatch *custObj, std::string name)
{
IHTMLDocument2 *doc = GetDoc();
IHTMLWindow2 *win = NULL;
doc->get_parentWindow(&win);

if (win == NULL) {
return;
}

IDispatchEx *winEx;
win->QueryInterface(&winEx);

if (winEx == NULL) {
return;
}

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name.c_str(), -1, NULL, 0);
BSTR objName = SysAllocStringLen(0, lenW);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name.c_str(), -1, objName, lenW);

DISPID dispid;
HRESULT hr = winEx->GetDispID(objName, fdexNameEnsure, &dispid);

SysFreeString(objName);

if (FAILED(hr)) {
return;
}

DISPID namedArgs[] = {DISPID_PROPERTYPUT};
DISPPARAMS params;
params.rgvarg = new VARIANT[1];
params.rgvarg[0].pdispVal = custObj;
params.rgvarg[0].vt = VT_DISPATCH;
params.rgdispidNamedArgs = namedArgs;
params.cArgs = 1;
params.cNamedArgs = 1;

hr = winEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &params, NULL, NULL, NULL);

if (FAILED(hr)) {
return;
}
}

上面的代码一直运行,所以到目前为止一切看起来都很好。

当我收到作为 *custObj 传递的 DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 事件时调用 AddCustomObject:

class JSObject : public IDispatch {
private:
long ref;

public:
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();

// IDispatch
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo);
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid,
ITypeInfo **ppTInfo);
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,
LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS *Params, VARIANT *pVarResult,
EXCEPINFO *pExcepInfo, UINT *puArgErr);
};

值得注意的实现可能是

HRESULT STDMETHODCALLTYPE JSObject::QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL;

if (riid == IID_IUnknown || riid == IID_IDispatch) {
*ppv = static_cast<IDispatch*>(this);
}

if (*ppv != NULL) {
AddRef();
return S_OK;
}

return E_NOINTERFACE;
}

HRESULT STDMETHODCALLTYPE JSObject::Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS *Params, VARIANT *pVarResult,
EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
MessageBox(NULL, "Invoke", "JSObject", MB_OK);
return DISP_E_MEMBERNOTFOUND;
}

不幸的是,当我尝试使用 javascript 代码中的“JSObject”对象时,我从未收到“Invoke”消息框。

JSObject.randomFunctionName();  // This should give me the c++ "Invoke" message
// box, but it doesn't

编辑 2:

我像这样实现了 GetIDsOfNames:

HRESULT STDMETHODCALLTYPE JSObject::GetIDsOfNames(REFIID riid,
LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
HRESULT hr = S_OK;

for (UINT i = 0; i < cNames; i++) {
std::map<std::wstring, DISPID>::iterator iter = idMap.find(rgszNames[i]);
if (iter != idMap.end()) {
rgDispId[i] = iter->second;
} else {
rgDispId[i] = DISPID_UNKNOWN;
hr = DISP_E_UNKNOWNNAME;
}
}

return hr;
}

这是我的构造函数

JSObject::JSObject() : ref(0)
{
idMap.insert(std::make_pair(L"execute", DISPID_USER_EXECUTE));
idMap.insert(std::make_pair(L"writefile", DISPID_USER_WRITEFILE));
idMap.insert(std::make_pair(L"readfile", DISPID_USER_READFILE));
}

将 DISPID_USER_* 常量定义为私有(private)类成员

class JSObject : public IDispatch {
private:
static const DISPID DISPID_USER_EXECUTE = DISPID_VALUE + 1;
static const DISPID DISPID_USER_WRITEFILE = DISPID_VALUE + 2;
static const DISPID DISPID_USER_READFILE = DISPID_VALUE + 3;

// ...
};

编辑 3、4 和 5:

移至 a separate question

编辑 6:

制造 a separate question退出“返回字符串”编辑。这样我可以接受Georg's回复原问题。

编辑 7:

我收到了一些关于完全工作、自包含的示例实现的请求。这里是:https://github.com/Tobbe/CppIEEmbed .如果可以的话,请 fork 和改进:)

最佳答案

您需要实现 GetIDsOfNames()做一些明智的事情,因为该函数将在 Invoke() 之前由客户端代码调用.
如果您的接口(interface)在类型库中,请参阅 here例如。如果你想使用后期绑定(bind),你可以使用 DISPID s更大 DISPID_VALUE小于 0x80010000 (<= 00x800100000x8001FFFF 范围内的所有值都是保留的):

HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, 
LCID lcid, DISPID *rgDispId)
{
HR hr = S_OK;
for (UINT i=0; i<cNames; ++i) {
if (validName(rgszNames)) {
rgDispId[i] = dispIdForName(rgszNames);
} else {
rgDispId[i] = DISPID_UNKNOWN;
hr = DISP_E_UNKNOWNNAME;
}
}
return hr;
}

HRESULT Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS *Params, VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
UINT *puArgErr)
{
if (wFlags & DISPATCH_METHOD) {
// handle according to DISPID ...
}

// ...

请注意 DISPID s 不应该突然改变,例如静态map或应使用常量值。

关于javascript - 从运行在 Web 浏览器控件中的 JavaScript 脚本调用 C++ 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3747414/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com