2010-09-19 26 views
12

He incorporado un control de navegador web en mi aplicación C++. Quiero que javascript se ejecute en el control del navegador web para poder llamar a una función/método de C++.Llamar a la función C++ desde el script JavaScript que se ejecuta en un control de navegador web

he encontrado menciones de tres maneras de hacer esto:

  1. implementar un componente ActiveX que actúa como un intermediario. (Detalles de implementación aquí: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
  2. Use window.external. (También se discutió en el enlace de arriba, pero sin aplicación proporciona)
  3. Añadir un objeto personalizado al objeto de ventana

Quiero ir con la tercera opción, pero no he encontrado ningún ejemplo de trabajo sobre cómo Para hacer eso. ¿Alguien puede mostrarme cómo hacerlo, o un enlace a un ejemplo de trabajo en la red en alguna parte?

Lo más parecido a un ejemplo que he encontrado es la primera respuesta de Igor Tandetnik en a thread in the webbrowser_ctl news group. Pero me temo que necesito más ayuda que eso.

Estoy incorporando un control IWebBrowser2 y no estoy usando MFC, ATL o WTL.

EDIT:

A juzgar por el pseudo-código dado por Igor en el hilo he vinculado anteriormente, y el código que se encuentra en el artículo CodeProject "Creating JavaScript arrays and other objects from C++" He producido algún código.

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

El código anterior se ejecuta todo el camino a través de, por lo que todo se ve bien tan lejos.

llamo AddCustomObject cuando recibo el evento DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 pasar esto como *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); 
}; 

implementaciones notables podrían ser

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

y

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

Por desgracia no tengo la Cuadro de mensaje "Invocar" w Cuando trato de usar el objeto "JSObject" desde el código javascript.

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

EDIT 2:

he implementado GetIDsOfNames así:

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

y este es mi constructor

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 el DISPID_USER_ * constantes definen como clase privada miembros

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; 

    // ... 
}; 

EDITAR 3, 4 y 5:

Trasladado a a separate question

EDITAR 6:

Made a separate question de las ediciones "que regresan una cadena". De esa forma puedo aceptar la respuesta Georg's ya que responde a la pregunta original.

EDITAR 7:

He conseguido algunas peticiones para un totalmente funcional, independiente, ejemplo de implementación. Aquí está: https://github.com/Tobbe/CppIEEmbed. Por favor bifurca y mejora si puedes :)

+0

¿'GetIDsOfNames()' devuelve algo sensato? –

+0

@Georg: No, no es así. Simplemente devuelve E_FAIL. – Tobbe

+0

Ahora estoy corriendo en un problema similar al tuyo ... teniendo en cuenta que has logrado hacerlo, ¿considerarías poner un ejemplo de trabajo disponible para descargar en alguna parte? – titel

Respuesta

5

Tienes que implementar GetIDsOfNames() para hacer algo sensato ya que esa función será llamada por el código del cliente antes de Invoke().
Si tiene sus interfaces en una biblioteca de tipos, consulte here para ver un ejemplo. Si desea utilizar enlace tardío lugar, puede utilizar DISPIDs mayor DISPID_VALUE y menos de 0x80010000 (todos los valores <= 0 y en el rango 0x80010000 través 0x8001FFFF está reservado):

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

    // ... 

Tenga en cuenta que no se supone que los DISPID s cambiar de repente, por ejemplo Se debe usar un valor map estático o valores constantes.

+0

Ese ejemplo, y otros ejemplos que he encontrado, hacen uso de la función DispGetIDsOfNames que necesita un puntero "ITypeInfo". ¿Qué es esto de ITypeInfo y cómo puedo crear/obtener uno que pueda usar en mi caso? – Tobbe

+0

Los ID (DISPID) que devuelvo en GetIDsOfNames, ¿solo se usan en Invoke()? O se usan en más lugares. Lo que realmente quiero saber es si puedo inventarlos yo mismo, o si tengo que usar algo como GetDispID para generarlos para mí. (El uso de identificadores inventados/aleatorios parece funcionar, pero solo quiero saber si hacerlo es una mala idea o no) – Tobbe

Cuestiones relacionadas