2010-09-19 54 views
12

Tôi đã nhúng điều khiển trình duyệt web trong ứng dụng C++ của mình. Tôi muốn javascript chạy trong điều khiển trình duyệt web để có thể gọi một hàm/phương thức C++.Gọi hàm C++ từ tập lệnh JavaScript chạy trong điều khiển trình duyệt web

Tôi đã tìm thấy đề cập đến trong ba cách để làm điều này:

  1. Thực hiện một thành phần ActiveX hoạt động như một người đàn ông trung. (Chi tiết triển khai tại đây: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
  2. Sử dụng window.external. (Ngoài ra thảo luận trong diễn đàn, nhưng không thực hiện được cung cấp)
  3. Thêm một đối tượng tùy chỉnh để các đối tượng cửa sổ

Tôi muốn đi với các tùy chọn thứ ba, nhưng tôi đã không tìm thấy bất kỳ ví dụ làm việc trên như thế nào Để làm việc đó. Có thể ai đó vui lòng chỉ cho tôi cách thực hiện hoặc liên kết với ví dụ hoạt động trên mạng ở đâu đó.

Gần nhất với ví dụ mà tôi đã tìm thấy là câu trả lời đầu tiên của Igor Tandetnik trong a thread in the webbrowser_ctl news group. Nhưng tôi e rằng tôi cần được giúp đỡ nhiều hơn thế.

Tôi đang nhúng điều khiển IWebBrowser2 và tôi không sử dụng MFC, ATL hoặc WTL.

EDIT:

Going by the pseudo-code do Igor trong thread tôi liên kết trước đó, và mã tìm thấy trong bài viết CodeProject "Creating JavaScript arrays and other objects from C++" Tôi đã tạo ra một số 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; 
    } 
} 

Đoạn mã trên chạy toàn bộ, vì vậy mọi thứ sẽ ổn hoàn toàn.

tôi gọi AddCustomObject khi tôi nhận được sự kiện DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2 đi này như *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); 
}; 

hiện thực đáng chú ý có thể là

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

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

Đáng tiếc là tôi không bao giờ có được Hộp thông báo "Gọi" Tôi cố gắng sử dụng đối tượng "JSObject" từ mã javascript.

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

EDIT 2:

tôi thực hiện GetIDsOfNames như vậy:

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

và đây là nhà xây dựng

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

tôi với DISPID_USER_ * hằng số định nghĩa là lớp tin thành viên

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 và 5:

Chuyển đến a separate question

EDIT 6:

Made a separate question ra khỏi "trả về một chuỗi" chỉnh sửa. Bằng cách đó tôi có thể chấp nhận trả lời Georg's vì câu trả lời cho câu hỏi gốc.

EDIT 7:

Tôi đã nhận được một vài yêu cầu cho một, tự hoàn toàn làm việc kín, ví dụ thực hiện. Đây là: https://github.com/Tobbe/CppIEEmbed. Vui lòng ngã ba và cải thiện nếu bạn có thể :)

+0

'GetIDsOfNames()' có trả về một điều gì đó hợp lý không? –

+0

@Georg: Không, không. Nó chỉ trả về E_FAIL. – Tobbe

+0

Tôi hiện đang chạy trong một vấn đề tương tự như bạn ... xem xét rằng bạn đã quản lý để thực hiện điều này bạn sẽ xem xét việc làm một ví dụ làm việc có sẵn để tải về một nơi nào đó? – titel

Trả lời

5

Bạn cần triển khai GetIDsOfNames() để làm điều gì đó hợp lý vì chức năng đó sẽ được gọi bằng mã khách hàng trước Invoke().
Nếu bạn có giao diện trong thư viện kiểu, hãy xem here để biết ví dụ. Nếu bạn muốn sử dụng late-binding thay vào đó, bạn có thể sử dụng DISPIDs hơn DISPID_VALUE và ít hơn 0x80010000 (tất cả các giá trị <= 0 và trong phạm vi 0x80010000 qua 0x8001FFFF được dành riêng):

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

    // ... 

Lưu ý rằng DISPID s không phải là nghĩa thay đổi đột ngột, vd phải sử dụng giá trị tĩnh map hoặc giá trị không đổi.

+0

Ví dụ đó và các ví dụ khác mà tôi đã tìm thấy, hãy sử dụng hàm DispGetIDsOfNames cần một con trỏ "ITypeInfo". Điều này là gì ITypeInfo, và làm thế nào để tôi tạo/nhận được một trong những tôi có thể sử dụng trong trường hợp của tôi? – Tobbe

+0

Các ID (DISPID) Tôi trả về trong GetIDsOfNames, chúng chỉ được sử dụng trong Invoke()? Hay chúng được sử dụng ở nhiều nơi hơn. Những gì tôi thực sự muốn biết là nếu tôi chỉ có thể tự làm cho mình, hoặc nếu tôi phải sử dụng một cái gì đó như GetDispID để tạo ra chúng cho tôi. (Sử dụng các ID được tạo/ngẫu nhiên dường như hoạt động, nhưng tôi chỉ muốn biết nếu làm điều đó là một ý tưởng tồi hay không) – Tobbe

Các vấn đề liên quan