Mam teraz zweryfikowane podejście eval
działa konsekwentnie z IE9, IE10 i IE11 (sprawdza błędów pomijane dla breavity):
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));
Temp nawet lepiej niż execScript
, ponieważ faktycznie zwraca result
. To działa również w języku C# z WinForms' WebBrowser
:
var result = webBrowser1.Document.InvokeScript("eval", new object[] { "confirm('see this?')" });
MessageBox.Show(result.ToString());
Powiedział, że execScript
nadal pracuje dla IE11 Preview:
CComVariant result;
m_htmlWindow->execScript(CComBSTR(L"confirm('See this too?')"), CComBSTR(L"JavaScript"), &result);
result.ChangeType(VT_BSTR);
MessageBoxW(V_BSTR(&result));
i nadal odrzuca result
, jak zawsze.
Trochę nie na temat, ale nie musisz trzymać się tego za pomocą eval
. Takie podejście pozwala na wykonanie dowolnej nazwanej metody dostępnej w przestrzeni nazw obiektu JavaScript window
załadowanej strony (za pośrednictwem interfejsu IDispatch). Możecie nazywać swoją funkcję i przechodzą na żywo obiektu COM do niego, zamiast parametru ciąg, np .:
// JavaScript
function AlertUser(user)
{
alert(user.name);
return user.age;
}
// C++
CComDispatchDriver disp = m_htmlWindow; // of IHTMLWindow2
disp.Invoke1(L"AlertUser", &CComVariant(userObject), &result);
bym w miarę możliwości wolą powyższy bezpośredniego połączenia do eval
.
[edited]
To trwa kilka poprawek do tej pracy podejście do out-of-process połączeń. Jak zauważyli @JimEvans w komentarzach, Invoke
zwrócił błąd 0x80020006 ("Nieznana nazwa"). Jednak test HTA app działało dobrze, co sprawiło, że pomyślałem, aby spróbować IDispatchEx::GetDispId dla rozpoznawania nazw. Że rzeczywiście przepracowanych (sprawdza błędów pominięta):
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
Pełne C++ aplikacja test jest tutaj: http://pastebin.com/ccZr0cG2
[UPDATE]
Ta aktualizacja tworzy __execScript
sposób na window
obiektu dziecka iframe
, poza procesem.Kod, który ma zostać wstrzyknięty, został zoptymalizowany w celu zwrócenia obiektu docelowego w celu późniejszego użycia (nie ma potrzeby wykonywania serii wywołań poza proces w celu uzyskania obiektu iframe
, odbywa się to w kontekście głównego okna):
CComBSTR __execScriptCode(L"(window.__execScript = function(exp) { return eval(exp); }, window.self)");
Poniżej znajduje się kod aplikacji konsoli C++ (pastebin), niektóre pomyłki zostały pominięte w celu wykluczenia. Istnieje również odpowiednia prototype in .HTA, która jest bardziej czytelna.
//
// http://stackoverflow.com/questions/18342200/how-do-i-call-eval-in-ie-from-c/18349546//
//
#include <tchar.h>
#include <ExDisp.h>
#include <mshtml.h>
#include <dispex.h>
#include <atlbase.h>
#include <atlcomcli.h>
#define _S(a) \
{ HRESULT hr = (a); if (FAILED(hr)) return hr; }
#define disp_cast(disp) \
((CComDispatchDriver&)(void(static_cast<IDispatch*>(disp)), reinterpret_cast<CComDispatchDriver&>(disp)))
struct ComInit {
ComInit() { ::CoInitialize(NULL); }
~ComInit() { CoUninitialize(); }
};
int _tmain(int argc, _TCHAR* argv[])
{
ComInit comInit;
CComPtr<IWebBrowser2> ie;
_S(ie.CoCreateInstance(L"InternetExplorer.Application", NULL, CLSCTX_LOCAL_SERVER));
_S(ie->put_Visible(VARIANT_TRUE));
CComVariant ve;
_S(ie->Navigate2(&CComVariant(L"http://jsfiddle.net/"), &ve, &ve, &ve, &ve));
// wait for page to finish loading
for (;;)
{
Sleep(250);
READYSTATE rs = READYSTATE_UNINITIALIZED;
ie->get_ReadyState(&rs);
if (rs == READYSTATE_COMPLETE)
break;
}
// inject __execScript into the main window
CComPtr<IDispatch> dispDoc;
_S(ie->get_Document(&dispDoc));
CComPtr<IHTMLDocument2> htmlDoc;
_S(dispDoc->QueryInterface(&htmlDoc));
CComPtr<IHTMLWindow2> htmlWindow;
_S(htmlDoc->get_parentWindow(&htmlWindow));
CComPtr<IDispatchEx> dispexWindow;
_S(htmlWindow->QueryInterface(&dispexWindow));
CComBSTR __execScript("__execScript");
CComBSTR __execScriptCode(L"(window.__execScript = function(exp) { return eval(exp); }, window.self)");
DISPID dispid = -1;
_S(dispexWindow->GetDispID(CComBSTR("eval"), fdexNameCaseSensitive, &dispid));
_S(disp_cast(dispexWindow).Invoke1(dispid, &CComVariant(__execScriptCode)));
// inject __execScript into the child frame
WCHAR szCode[1024];
wsprintfW(szCode, L"document.all.tags(\"iframe\")[0].contentWindow.eval(\"%ls\")", __execScriptCode.m_str);
dispid = -1;
_S(dispexWindow->GetDispID(__execScript, fdexNameCaseSensitive, &dispid));
CComVariant vIframe;
_S(disp_cast(dispexWindow).Invoke1(dispid, &CComVariant(szCode), &vIframe)); // inject __execScript and return the iframe's window object
_S(vIframe.ChangeType(VT_DISPATCH));
CComPtr<IDispatchEx> dispexIframe;
_S(V_DISPATCH(&vIframe)->QueryInterface(&dispexIframe));
dispid = -1;
_S(dispexIframe->GetDispID(__execScript, fdexNameCaseSensitive, &dispid));
_S(disp_cast(dispexIframe).Invoke1(dispid, &CComVariant("alert(document.URL)"))); // call the code inside child iframe
return 0;
}
Domyślam się, że chcesz to zrobić jak chcesz w JavaScript, dodając nowy element skryptu do DOM strony, ale mam sprawdzanie z zespołem IE dev ... – EricLaw
@JimEvans , Nie mam zainstalowanego IE11, aby spróbować, ale następujące prace dla IE10 przy użyciu 'eval':' CComDispatchDriver window = m_window;/* z IHTMLWindow2 */ window.Invoke1 (L "eval", i CComVariant (L "alert (true)")); ' – Noseratio