Appel de la fonction C ++ à partir du script JavaScript exécuté dans un contrôle du navigateur Web

J'ai intégré un contrôle de navigateur Web dans mon application c ++. Je veux que javascript s'exécute dans le contrôle du navigateur Web pour pouvoir appeler une fonction / méthode c ++.

J'ai trouvé des mentions de trois façons de faire ceci:

  1. Implémentez un composant ActiveX qui agit comme un homme du milieu. (Détails de mise en œuvre ici: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx )
  2. Utiliser window.external. (Également abordé dans le lien ci-dessus, mais aucune mise en œuvre n'est fournie)
  3. Ajouter un objet personnalisé à l'objet fenêtre

Je veux aller avec la troisième option, mais je n'ai trouvé aucun exemple de travail sur la façon de le faire. Quelqu'un peut-il me montrer comment le faire ou lier un exemple de travail sur le net quelque part.

Le plus proche d'un exemple que j'ai trouvé est la première réponse d'Igor Tandetnik dans un fil du groupe de nouvelles webbrowser_ctl. Mais j'ai peur d'avoir plus d'aide que cela.

J'implacer un contrôle IWebBrowser2 et je n'utilise pas MFC, ATL ou WTL.

MODIFIER:

En passant par le pseudo-code donné par Igor dans le thread que j'ai lié plus tôt, et le code trouvé dans l'article codeprojet " Création de tableaux JavaScript et autres objets de C ++ " J'ai produit un certain code.

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

Le code ci-dessus se déroule tout au long, donc tout semble bien aussi loin.

J'appelle AddCustomObject lorsque je reçois l'événement DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 en passant comme *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); }; 

Des implémentations remarquables pourraient être

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

et

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

Malheureusement, je n'obtiens jamais la boîte de message "Invoke" lorsque j'essaie d'utiliser l'objet "JSObject" à partir du code javascript.

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

EDIT 2:

J'ai implémenté GetIDsOfNames comme ça:

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

Et c'est mon constructeur

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

Avec les constantes DISPID_USER_ * définies comme membres de classe privée

 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 et 5:

Passé à une autre question

EDIT 6:

Réalisé une question distincte de l'édition de "renvoyer une chaîne". De cette façon, je peux accepter la réponse de Georg car cela répond à la question initiale.

EDIT 7:

J'ai eu quelques requêtes pour une mise en œuvre d'un travail complet et autonome. Voici: https://github.com/Tobbe/CppIEEmbed . Prévoyez et améliorez si vous pouvez 🙂

Vous devez implémenter GetIDsOfNames() pour faire quelque chose de raisonnable car cette fonction sera appelée par le code client avant Invoke() .
Si vous avez vos interfaces dans une bibliothèque de types, voir ici pour un exemple. Si vous souhaitez utiliser la liaison tardive à la place, vous pouvez utiliser DISPID s plus grande DISPID_VALUE et inférieure à 0x80010000 (toutes les valeurs <= 0 et dans la plage 0x80010000 à 0x8001FFFF sont réservées):

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

Notez que les DISPID s ne sont pas supposés changer brusquement, donc, par exemple, une map statique ou des valeurs constantes devraient être utilisées.