2010-09-19 5 views
12

Ho incorporato un controllo browser Web nella mia applicazione C++. Voglio javascript in esecuzione nel controllo del browser Web per essere in grado di chiamare una funzione/metodo C++.Chiamata funzione C++ da script JavaScript in esecuzione in un controllo browser Web

ho trovato menzioni di tre modi per farlo:

  1. implementare un componente ActiveX che agisce come un uomo di mezza. (Dettagli di implementazione qui: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
  2. Utilizzare window.external. (Discusso anche nel link qui sopra, ma nessuna implementazione fornito)
  3. Aggiungi un oggetto personalizzato per l'oggetto window

voglio andare con la terza opzione, ma non ho trovato alcun esempio di lavoro su come fare quello. Qualcuno può mostrarmi come farlo, o collegare un esempio funzionante sulla rete da qualche parte.

Il più vicino a un esempio che ho trovato è la prima risposta di Igor Tandetnik in a thread in the webbrowser_ctl news group. Ma temo di aver bisogno di più aiuto di quello.

Sto incorporando un controllo IWebBrowser2 e non sto utilizzando MFC, ATL o WTL.

EDIT:

Andando dalla pseudo-codice data di Igor nel thread ho collegato in precedenza, e il codice trovato in questo articolo CodeProject "Creating JavaScript arrays and other objects from C++" Ho prodotto un certo codice.

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; 
    } 
} 

Il codice sopra scorre fino in fondo, quindi tutto sembra a posto.

chiamo AddCustomObject quando ricevo l'evento DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 passare questo come *custObj:

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); 
}; 

implementazioni degne di nota potrebbero essere

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; 
} 

e

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; 
} 

Purtroppo non ho mai ottenere il Casella di messaggio "Invoke" w quando provo a usare l'oggetto "JSObject" dal codice javascript.

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

EDIT 2:

ho implementato GetIDsOfNames in questo modo:

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; 
} 

e questo è il mio costruttore

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)); 
} 

con la DISPID_USER_ * costanti definite come classe privata membri

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; 

    // ... 
}; 

EDIT 3, 4 e 5:

Spostato a separate question

EDIT 6:

Realizzato a separate question fuori dei "restituendo una stringa" modifiche. In questo modo posso accettare la risposta Georg's in quanto risponde alla domanda originale.

EDIT 7:

ho ottenuto un paio di richieste per una piena di lavoro, autonomo, esempio di implementazione. Eccolo: https://github.com/Tobbe/CppIEEmbed. Si prega di fork e migliorare se è possibile :)

+0

'GetIDsOfNames()' restituisce qualcosa di sensato? –

+0

@ Georg: No, non è così. Restituisce solo E_FAIL. – Tobbe

+0

Ora sto correndo in un problema simile a quello che stavi ... considerando che sei riuscito a fare questo ti prenderebbe in considerazione la possibilità di rendere un esempio funzionante disponibile per il download da qualche parte? – titel

risposta

5

È necessario implementare GetIDsOfNames() per fare qualcosa di ragionevole in quanto tale funzione verrà chiamata dal codice client prima Invoke().
Se si dispone delle interfacce in una libreria dei tipi, vedere here per un esempio. Se si desidera utilizzare late-binding, invece, è possibile utilizzare DISPIDs maggiore DISPID_VALUE e meno di 0x80010000 (tutti i valori <= 0 e nella gamma 0x80010000 attraverso 0x8001FFFF sono riservati):

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 ... 
    } 

    // ... 

Nota che i DISPID s non sono supposti cambiare improvvisamente, quindi ad es deve essere utilizzato un valore statico map o valori costanti.

+0

Quell'esempio e altri esempi che ho trovato utilizzano la funzione DispGetIDsOfNames che ha bisogno di un puntatore "ITypeInfo". Cos'è questa cosa ITypeInfo e come posso crearne/ottenerne una che possa essere utilizzata nel mio caso? – Tobbe

+0

Gli ID (DISPID) I restituiscono in GetIDsOfNames, sono utilizzati solo in Invoke()? O sono usati in più posti. Quello che voglio veramente sapere è se riesco a inventarli da solo, o se devo usare qualcosa come GetDispID per generarli per me. (Usare gli ID composti/casuali sembra funzionare, ma voglio solo sapere se farlo è una cattiva idea o no) – Tobbe

Problemi correlati