首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 呼叫 JavaScript 代碼完整方案:IE 執行緒與跨執行緒調用實作

     編輯:關於C++

在 COM 元件與 Web 前端互動場景中,C++ 呼叫 JavaScript 函式分為**同執行緒(IE 主執行緒)**與**跨執行緒**兩種情境。本文詳細說明兩種調用機制與完整程式碼實作。

核心機制:IDispatch 介面

JavaScript 函式在 COM 中以 IDispatch 介面暴露,C++ 透過 Invoke / InvokeN 調用。關鍵型別:VARIANT 承載參數與回傳值。

情境一:IE 主執行緒直接調用

適用於 BHO、ActiveX 控制項等**運行於 IE 進程主執行緒**的元件。

C++ 實作

STDMETHODIMP CJsInvoker::InvokeJsFunc(LONG para1, LONG para2,
                                      VARIANT jsFunction, LONG* retValue) {
    // 1. 從 VARIANT 取得 IDispatch 指標
    CComPtr<IDispatch> jsCallback;
    if (jsFunction.vt == VT_DISPATCH)
        jsCallback = jsFunction.pdispVal;
    else
        return E_INVALIDARG;

    // 2. 準備參數陣列(注意:IDispatch::Invoke 參數順序為反序)
    VARIANT arg[2];
    arg[0].vt = VT_I4; arg[0].lVal = para2;  // 最後參數放索引 0
    arg[1].vt = VT_I4; arg[1].lVal = para1;  // 最先參數放索引 1

    // 3. 呼叫 Invoke
    DISPPARAMS dispParams = { arg, nullptr, 2, 0 };
    VARIANT result;
    VariantInit(&result);
    HRESULT hr = jsCallback->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT,
                                    DISPATCH_METHOD, &dispParams, &result, nullptr, nullptr);

    if (SUCCEEDED(hr) && result.vt == VT_I4)
        *retValue = result.lVal;
    VariantClear(&result);
    return hr;
}

JavaScript 端

<script type="text/javascript">
function jsCallbackFunc(a, b) { return a + b; }

var obj = new ActiveXObject("ComCallJsFunction.JsInvoker");
var ret = obj.InvokeJsFunc(1, 2, jsCallbackFunc);
alert(ret);  // 顯示 3
</script>

情境二:非 IE 執行緒(背景執行緒)調用

背景執行緒無法直接存取 JS 物件(STA 單元執行緒模型),必須**編列介面指標**到目標執行緒,再**反編列**取得代理指標。

關鍵 API

  • CoMarshalInterThreadInterfaceInStream:將介面指標編列為資料流
  • CoGetInterfaceAndReleaseStream:從資料流反編列取得代理指標
  • CoInitialize/CoUninitialize:執行緒 COM 單元初始化/清理

C++ 完整實作

// 主執行緒:編列介面並啟動背景執行緒
STDMETHODIMP CJsInvoker::InvokeJsFunc3(LONG para1, LONG para2,
                                       VARIANT jsFunction, LONG* retValue) {
    if (V_VT(&jsFunction) != VT_DISPATCH || jsFunction.pdispVal == NULL)
        return E_INVALIDARG;

    // 編列 IDispatch 到資料流
    HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_IDispatch,
                                                       jsFunction.pdispVal,
                                                       &m_stream_jsfunc);
    if (FAILED(hr)) return hr;

    // 啟動背景執行緒
    m_hThread = CreateThread(nullptr, 0, ThreadFunction, this, 0, nullptr);
    return S_OK;
}

// 背景執行緒函式
DWORD WINAPI CJsInvoker::ThreadFunction(LPVOID pParam) {
    CoInitialize(nullptr);  // STA 初始化

    CJsInvoker* pThis = static_cast<CJsInvoker*>(pParam);
    CComPtr<IDispatch> script;

    // 反編列取得代理指標
    HRESULT hr = CoGetInterfaceAndReleaseStream(pThis->m_stream_jsfunc,
                                                IID_IDispatch,
                                                reinterpret_cast<LPVOID*>(&script));
    if (SUCCEEDED(hr)) {
        VARIANT arg[2] = { {VT_I4, 0, 0, 1}, {VT_I4, 0, 0, 2} };
        DISPPARAMS dispParams = { arg, nullptr, 2, 0 };
        VARIANT result;
        VariantInit(&result);
        script->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT,
                       DISPATCH_METHOD, &dispParams, &result, nullptr, nullptr);
        VariantClear(&result);
    }

    CoUninitialize();
    return 0;
}

JavaScript 端(同情境一)

注意:背景執行緒調用為**非同步**,JS 回傳值無法直接傳回 C++ 呼叫端,需透過回呼、事件或共享記憶體傳遞結果。

關鍵差異總結

特性IE 主執行緒背景執行緒
COM 單元模型已在 STA 內需自行 CoInitialize
介面指標直接使用需編列/反編列
同步性同步阻塞非同步
回傳值處理直接取得需額外機制
適用場景BHO、工具列、同步自動化長時間運算、非阻塞 UI

現代替代方案

  • WebView2 (Edge Chromium):CoreWebView2.ExecuteScriptAsync 非同步執行 JS,支援 JSON 序列化參數/回傳值。
  • CEF (Chromium Embedded Framework):CefV8Context::Eval 同步/非同步調用。
  • Node.js Native Addons (NAPI):C++ 與 JS 共享記憶體、直接函式呼叫,效能最佳。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved