2010-09-19 10 views
12

Bir web tarayıcısı denetimini C++ uygulamasına yerleştirdim. Bir C++ işlevi/yöntemini çağırabilmek için web tarayıcısında javascript çalışmasını istiyorum. Bir web tarayıcısı denetiminde çalışan JavaScript komut dosyasından C++ işlevi çağrısı

  1. bir aracı olarak gören bir ActiveX bileşeni Uygulanışı: Ben bulduk

    Bunu yapmak için üç yöntemden bahseder. (Uygulama ayrıntıları burada: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)

  2. window.external öğesini kullanın. (Ayrıca yukarıdaki bağlantıyı tartışılan, ancak hiçbir uygulama sağlanan)

Ben üçüncü seçeneği ile gitmek istediğiniz pencere nesnesine özel bir nesne ekleme, ama nasıl herhangi bir çalışma örneklerini bulamadı bunu yapmak için. Birisi bana nasıl yapılacağını gösterir, ya da ağ üzerinde çalışan bir örneğe bağlantı verebilir.

Bulduğum örnek en yakın olan, ilk cevap Igor Tandetnik tarafından a thread in the webbrowser_ctl news group. Ama korkarım ki bundan daha fazla yardıma ihtiyacım var.

Bir IWebBrowser2 denetimi yerleştiriyorum ve MFC, ATL veya WTL kullanmıyorum.

DÜZENLEME: codeproject makalesinde bulunan daha önce bağlantılı dizisindeki Igor tarafından verilen sözde kod ve kod tarafından gidiş

"Creating JavaScript arrays and other objects from C++" Bazı kod hazırladım.

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

Yukarıdaki kod tüm yol boyunca çalışır, bu nedenle her şey bu kadar uzak görünüyor.

Ben *custObj olarak bu geçen DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 olayını aldığınızda ben AddCustomObject çağırır:

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

Kayda Değer uygulamaları Ne yazık ki asla

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

ve

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

olabilir "Çağır" mesaj kutusu w hen javascript kodundan "JSObject" nesnesini kullanmaya çalışıyorum.

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

DÜZENLEME 2:

ben şöyle GetIDsOfNames uygulanan:

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

ve bu * DISPID_USER_ ile benim yapıcı

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

olan sabitler özel sınıf olarak tanımlanan üyeler

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; 

    // ... 
}; 

DÜZENLEME 3, 4 ve 5:

taşındı 6 DÜZENLEME

a separate question için:

"bir dize dönen" düzenlemeleri dışında a separate question yapılmıştır. Bu şekilde Georg's cevabını kabul edip, orijinal soruya cevap verebiliyorum.

DÜZENLEME 7: Ben içerdiği tam kapasiteyle çalışan, kendini, örneğin uygulama için bazı istekleri aldık

. İşte burada: https://github.com/Tobbe/CppIEEmbed. Lütfen çatabilir ve geliştirebilirsiniz :)

+0

'GetIDsOfNames()' bir şeyleri mantıklı döndürüyor mu? –

+0

@Georg: Hayır, öyle değil. Sadece E_FAIL değerini döndürür. – Tobbe

+0

Şu anki gibi benzer bir sorunla karşılaşıyorum ... başardığınızı düşündüğünüzde bunu başarmak için bir çalışma örneği hazırlamayı düşünür müsünüz? – titel

cevap

5

Invoke()'dan önce istemci kodu tarafından bu işlev çağrılacağından mantıklı bir şey yapmak için GetIDsOfNames()'u uygulamanız gerekir.
Arabirimlerinizi bir tür kitaplığında varsa, örnek için here'a bakın. yerine geç bağlama kullanmak isterseniz, DISPIDs daha DISPID_VALUE ve az 0x80010000 kullanabilirsiniz (bütün değerler <= 0 ve aralıkta 0x8001FFFF aracılığıyla 0x80010000 saklıdır): DISPID s mayacağıdır

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

    // ... 

Not aniden değiştirmek için Statik map veya sabit değerler kullanılmalıdır.

+0

'daki kodlara bakmanız gerekir. Bu örnek ve bulduğum diğer örnekler, DispGetIDsOfNames işlevini kullanır "ITypeInfo" işaretçisi gerekiyor. Bu ITypeInfo olayı nedir ve vakamda kullanabileceğim birini nasıl oluşturabilirim? – Tobbe

+0

GetIDsOfNames'te döndürdüğüm Kimlikler (DISPID), yalnızca Invoke()? Ya da daha çok yerde kullanılıyorlar. Gerçekten bilmek istediğim, eğer bunları kendim yapabilirsem ya da GetDispID gibi bir şeyi benim için üretmem gerekirse. (Oluşturulan/rasgele kimlikleri kullanmak işe yarıyor gibi görünüyor, ama sadece bunun kötü bir fikir olup olmadığını bilmek istiyorum) – Tobbe

İlgili konular