2013-08-20 8 views
12

आईई 11 के आगमन के साथ, IHTMLWindow2::execScript() बहिष्कृत है। अनुशंसित दृष्टिकोण use eval() instead है। मैं आईई को अपने सी ++ कॉम इंटरफेस के माध्यम से स्वचालित कर रहा हूं, और मैं इसे पूरा करने में असमर्थ हूं। क्या कोई मुझे उदाहरण के लिए इंगित कर सकता है जिसे मैंने स्पष्ट रूप से मेरी खोज में याद किया है? यदि eval के माध्यम से कोड निष्पादित करना संभव नहीं है, तो इंटरनेट एक्सप्लोरर के चल रहे उदाहरण में जावास्क्रिप्ट कोड इंजेक्ट करने का उचित तरीका क्या है अब execScript अब उपलब्ध नहीं है?मैं सी ++ से IE में eval() को कैसे कॉल करूं?

संपादित करें: कोई भी समाधान जो परियोजना के लिए काम करेगा, मैं काम कर रहा हूं, प्रक्रिया से बाहर काम करना चाहिए। मैं ब्राउज़र हेल्पर ऑब्जेक्ट (बीएचओ), या किसी भी प्रकार की आईई प्लगइन का उपयोग नहीं कर रहा हूं। इस प्रकार, कोई भी समाधान जिसमें एक इंटरफ़ेस शामिल है जिसे सही तरीके से मार्शल क्रॉस-प्रोसेस नहीं किया जा सकता है, वह मेरे लिए काम नहीं करेगा।

+1

मेरा अनुमान है कि आप इसे पसंद आप जावास्क्रिप्ट में होगा करना चाहते हैं, पेज के डोम के लिए एक नई स्क्रिप्ट तत्व जोड़कर है, लेकिन मैं IE देव टीम के साथ की जाँच कर रहा हूँ ... – EricLaw

+1

@JimEvans , मेरे पास IE11 को स्थापित करने के लिए इंस्टॉल नहीं किया गया है, लेकिन 'eval' का उपयोग करके IE10 के लिए निम्न कार्य करता है: 'CComDispatchDriver window = m_window;/* IHTMLWindow2 */ विंडो। इन्वोक 1 (एल "eval", और CComVariant (एल "चेतावनी (सच)"); ' – Noseratio

उत्तर

12

अब मैं सत्यापित eval दृष्टिकोण IE9, IE10 और IE11 के साथ लगातार काम करता है (त्रुटि जाँच 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)); 

execScript से भी बेहतर Feels, क्योंकि यह वास्तव में result देता है। यह WebBrowser WinForms के साथ सी # में भी काम करता है:

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 से चिपकने की आवश्यकता नहीं है। यह दृष्टिकोण लोड किए गए पृष्ठ (IDispatch इंटरफ़ेस के माध्यम से) के window ऑब्जेक्ट के नामस्थान के अंदर उपलब्ध किसी नामित विधि को निष्पादित करने की अनुमति देता है। आप अपनी खुद की समारोह कॉल कर सकते हैं और नहीं बल्कि एक स्ट्रिंग पैरामीटर की तुलना में यह में एक जीवित 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 के लिए ऊपर प्रत्यक्ष कॉल पसंद करेंगे।

[संपादित]

यह बाहर के प्रक्रिया कॉल के लिए इस दृष्टिकोण काम करने के लिए कुछ तोड़ मरोड़ लेता है। @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 

पूर्ण सी ++ परीक्षण ऐप आ गया है: http://pastebin.com/ccZr0cG2

[अद्यतन]

यह अद्यतन एक बच्चे की एक window वस्तु पर __execScript विधि बनाता है iframe, बाहर की proc।इंजेक्शन वाले कोड को window ऑब्जेक्ट को बाद में उपयोग के लिए लक्ष्य को वापस करने के लिए अनुकूलित किया गया था (iframe ऑब्जेक्ट प्राप्त करने के लिए आउट ऑफ़ ऑफ प्रो कॉल की श्रृंखला बनाने की आवश्यकता नहीं है, यह मुख्य विंडो के संदर्भ में किया जाता है):

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

नीचे C++ कंसोल ऐप (pastebin) के लिए कोड है, कुछ त्रुटि जांच ब्रेवटी के लिए छोड़ दी गई है। एक संबंधित prototype in .HTA भी है, जो अधिक पठनीय है।

// 
// 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; 
} 
+0

आप किसी नामित फ़ंक्शन को कॉल करने के बारे में बिल्कुल सही हैं। मेरी परियोजना पूरी तरह से दृष्टिकोण है। हालांकि, यह उस पृष्ठ को इंजेक्शन कर रहा है जिस पृष्ठ पर हम वर्तमान में 'execScript' का उपयोग कर रहे हैं, और जिसे प्रतिस्थापित करने की आवश्यकता है। एनबी, हमारे पास कोई भी नियंत्रण नहीं है कि हमारी लाइब्रेरी के किस पृष्ठ का उपयोग किया जा सकता है, इसलिए हम सीधे वैश्विक फ़ंक्शन बनाने के लिए पृष्ठ के स्रोत को संशोधित नहीं कर सकते हैं। – JimEvans

+0

इस मामले में आप एक नया वैश्विक फ़ंक्शन इंजेक्ट करने के लिए पहले 'eval' का उपयोग कर सकते हैं, इसे कॉल करें। यह काम करता है: 'disp.Invoke1 (एल" eval ", और CComVariant (एल" समारोह ग्लोबलटेस्ट() {चेतावनी (सच);} "));' – Noseratio

+0

आपकी प्रक्रिया की चिंता के संबंध में, मैंने कोशिश नहीं की है बाहर की proc, लेकिन मुझे विश्वास है कि यह काम करेगा। यह 'आईडीस्पैच' के माध्यम से किया जाता है जो स्वचालित रूप से मार्शल हो जाता है। कोई अतिरिक्त नलसाजी की जरूरत है। – Noseratio

संबंधित मुद्दे