原文地址:C++代碼中回調JS方法
前段時間開發了一個COM組件配合web前端使用,遇到了C++中調用JS代碼的問題,在網上查了很多資料,現總結一下,留作以後察看。
C++中調用JS代碼主要有兩種情況:1. IE線程中調用;2. 其他線程調用
1. IE線程中調用:這種情況網上已經有很多資料,下面列出示列代碼:
STDMETHODIMP CJsInvoker::InvokeJsFunc(LONG para1, LONG para2, VARIANT jsFunction, LONG* retValue){ CComPtr<IDispatch> jsCallback; if (jsFunction.vt == VT_DISPATCH) jsCallback = jsFunction.pdispVal; VARIANT arg[2]; arg[0].vt = VT_I4; arg[1].vt = VT_I4; arg[0].lVal = para1; arg[1].lVal = para2; VARIANT pvarRet; jsCallback.InvokeN(static_cast<DISPID>(DISPID_VALUE), arg, 2, &pvarRet); *retValue = pvarRet.lVal; return S_OK;}
<script type="text/javascript"> // 兩個參數的回調方法 function jsCallbackFunc(a, b) { return a + b; } var obj = new ActiveXObject("ComCallJsFunction.JsInvoker"); var retValue = objA.InvokeJsFunc(1, 2, jsCallbackFunc); alert(retValue); // 傳回值為3 </script>
從代碼中可以看出,Js方法作為IDispatch指標傳入COM,C++通過調用其InvokeN方法實現。
2. 其他線程調用:與IE線程直接調用的區別在於需要列集與反列集,原因是JS代碼是運行在自己的套間線程裡的,其他線程是不能直接存取的,只能通過代理進入訊息迴圈中。
STDMETHODIMP CJsInvoker::InvokeJsFunc3(LONG para1, LONG para2, VARIANT jsFunction, LONG* retValue){ // Check whether is valid Dispatch interface. if (V_VT(&jsFunction) != VT_DISPATCH || jsFunction.pdispVal == NULL) { return E_INVALIDARG; } // 對IDispatch指標列集 CoMarshalInterThreadInterfaceInStream(IID_IDispatch, jsFunction.pdispVal, &m_stream_jsfunc); m_hTread = CreateThread(NULL, 0, ThreadFunction, this, NULL, NULL); return S_OK;}DWORD WINAPI ThreadFunction(LPVOID pParam){ ::CoInitialize(NULL); CJsInvoker* pJsInvoker = (CJsInvoker*)pParam; CComPtr<IDispatch> script; // 反列集得到IDisPatch指標 CoGetInterfaceAndReleaseStream(pJsInvoker->m_stream_jsfunc, IID_IDispatch, (LPVOID *)&script); VARIANT arg[2]; arg[0].vt = VT_I4; arg[1].vt = VT_I4; arg[0].lVal = 1; arg[1].lVal = 2; VARIANT pvarRet; script.InvokeN(static_cast<DISPID>(DISPID_VALUE), arg, 2, &pvarRet); ::CoUninitialize(); return S_OK;}
<script type="text/javascript"> // 兩個參數的回調方法 function jsCallbackFunc(a, b) { return a + b; } var obj = new ActiveXObject("ComCallJsFunction.JsInvoker"); var retValue = objA.InvokeJsFunc(1, 2, jsCallbackFunc); alert(retValue); // 此時傳回值沒有意義 </script>
套間模式多線程
apartment model multi-threading(套間模式多線程)
COM在Windows 95和Windows NT中支援一種稱為套間模式(apartment model)的多執行緒技術。套間通常用來描述一個帶有支援COM對象的訊息佇列的線程。
原文地址:ATL之什麼是套間
【什麼是套間】
套間(APARTMENT)是什嗎?套間是線程的執行環境。根據這個,我一條一條來解釋一下跟套間有關的文章裡一些名詞和術語是個什麼意思。
1、STA,單線程套間,真實意思是單線程執行環境。每個線程都有個自己的套間。
2、MTA,多線程套間,真實意思是多線程執行環境。所有線程共用一個MTA。
3、STA和MTA的差別是什嗎?除了STA和MTA的共同點之外,STA建立了一個視窗,通過視窗的訊息機制來保證本線程COM對象方法的同步調用,MTA基本什麼也不做。這是唯一的差別。
4、STA和MTA的共同點是什嗎?都提供通過Proxy訪問其它套間的服務,都提供其它套間訪問自己時的Stub服務。
5、每個線程都有屬於一個套間,這句話的意思是每個STA線程都有一個自己的COM執行環境,每個MTA共用一個COM執行環境。所以在COM的世界裡,線程有三種,一種是普通線程,第二種是STA線程,第三種是MTA線程。所以套間也可以看成是自己的屬性。
6、每個COM對象有各自的套間屬性,共有5類,其中一個是在2000下專屬的,可直接無視,所以有以下4類:Single、Apartment、Both、Free。Single表示本COM只能存在於主STA線程,Apartment表示本COM對象只能存在於STA線程,Both表示本對象既可存在於STA線程,也可以存在於MTA線程,Free表示本COM對象只能存在於MTA線程。
講到這裡,其實已經很清楚了。套間這個東西,的確是很難理解的一個東西,只要把握住“線程的執行環境”,就好理解了。
原文地址:【跨套間訪問】
COM對象介面只能在COM線程中調用,COM允許從一個套間中引出介面,並且引用到另一個套間中,使得對象的套間之外也能看到套間的介面。
COM使用一種稱為列集(Marshal)的技術,允許介面指標可以被跨越套間邊界傳遞出去。
列集一個介面指標,實際上只是簡單地把介面指標變換成一個可被傳輸的位元組流,散集這個位元組流,就能還原出介面指標。當散集發生在同一套間的時候,散集出來的指標指向實際的對象;當散集發生在不同套間的時候,散集出來的指標實際指向的一是一個代理指標,通過這個代理指標,保證了COM對象的執行緒安全性。
以上三段小文字言簡意賅地介紹了跨套間訪問的散集/列集技術。如有未明,請查看《COM本質論》。