如何在C ++中调用IE中的eval()?

时间:2013-08-20 18:15:49

标签: c++ internet-explorer com

随着IE11的出现,IHTMLWindow2::execScript()已被弃用。建议的方法是use eval() instead。我通过其C ++ COM接口自动化IE,我一直无法找到如何实现这一点。有人能指出我在搜索中明显错过的例子吗?如果无法通过eval执行代码,那么现在execScript不再可用,将JavaScript代码注入正在运行的Internet Explorer实例的方法是什么?

编辑:任何适用于我正在进行的项目的解决方案都必须在进程外进行。我没有使用浏览器助手对象(BHO)或任何类型的IE插件。因此,任何涉及无法正确编组交叉过程的接口的解决方案都不适用于我。

1 个答案:

答案 0 :(得分:13)

我现在已经验证eval方法与IE9,IE10和IE11一致(错误检查跳过了错误):

CComVariant result;
CComDispatchDriver disp = m_htmlWindow; // of IHTMLWindow2
disp.Invoke1(L"eval", &CComVariant(L"confirm('See this?')"), &result);
result.ChangeType(VT_BSTR);
MessageBoxW(V_BSTR(&result));

感觉甚至比execScript更好,因为它实际上会返回result。 它也可以在C#中使用WinForms'WebBrowser

var result = webBrowser1.Document.InvokeScript("eval", new object[] { "confirm('see this?')" });
MessageBox.Show(result.ToString());

那就是说,execScript仍适用于IE11预览:

CComVariant result;
m_htmlWindow->execScript(CComBSTR(L"confirm('See this too?')"), CComBSTR(L"JavaScript"), &result);
result.ChangeType(VT_BSTR);
MessageBoxW(V_BSTR(&result));

它仍然丢弃result,就像它一直一样。

有点偏离主题,但你不必坚持使用eval。此方法允许执行加载页面的JavaScript window对象的名称空间内可用的任何命名方法(通过IDispatch接口)。您可以调用自己的函数并将实时COM对象传递给它,而不是字符串参数,例如:

// JavaScript
function AlertUser(user)
{
  alert(user.name);
  return user.age;
}

// C++
CComDispatchDriver disp = m_htmlWindow; // of IHTMLWindow2
disp.Invoke1(L"AlertUser", &CComVariant(userObject), &result);

我希望在可能的情况下直接调用eval

<强> [EDITED]

需要进行一些调整才能使此方法适用于进程外调用。正如@JimEvans在评论中指出的那样,Invoke返回错误0x80020006(“未知名称”)。但是,test HTA app工作得很好,是什么让我考虑尝试IDispatchEx::GetDispId进行名称解析。确实有效(跳过错误检查):

CComDispatchDriver dispWindow;
htmlWindow->QueryInterface(&dispWindow);

CComPtr<IDispatchEx> dispexWindow;
htmlWindow->QueryInterface(&dispexWindow);

DISPID dispidEval = -1;
dispexWindow->GetDispID(CComBSTR("eval"), fdexNameCaseSensitive, &dispidEval);
dispWindow.Invoke1(dispidEval, &CComVariant("function DoAlert(text) { alert(text); }")); // inject

DISPID dispidDoAlert = -1;
dispexWindow->GetDispID(CComBSTR("DoAlert"), fdexNameCaseSensitive, &dispidDoAlert) );
dispWindow.Invoke1(dispidDoAlert, &CComVariant("Hello, World!")); // call

完整的C ++测试应用程序位于:http://pastebin.com/ccZr0cG2

<强> [UPDATE]

此更新会在子进程__execScript的{​​{1}}对象上创建window方法。要注入的代码已经过优化,可以返回目标iframe对象供以后使用(不需要进行一系列的out-of-proc调用来获取window对象,它是在上下文中完成的。主窗口):

iframe

下面是C ++控制台应用程序(pastebin)的代码,跳过了一些错误检查。还有一个相应的prototype in .HTA,它更具可读性。

CComBSTR __execScriptCode(L"(window.__execScript = function(exp) { return eval(exp); }, window.self)");