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:
- 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)
- Use window.external. (También se discutió en el enlace de arriba, pero sin aplicación proporciona)
- 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, ¶ms, 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 :)
¿'GetIDsOfNames()' devuelve algo sensato? –
@Georg: No, no es así. Simplemente devuelve E_FAIL. – Tobbe
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