IDispatch介面主要應用於傳統的自動化編程,如著名的Microsoft Visual Basic。用戶端程式只需得到COM組件的IDispatch介面就可調用組件所有的屬性和方法。但IDispatch的局限在與它假定COM組件是靜態。也就是說,在運行期間,這些COM組件的屬性和方法是不能改變的。因此,如果要實現javascript和vbscript指令碼語言的動態特性,就需要一個更靈活的介面。
於是為指令碼語言量身定製的IDispatchEx介面誕生了。IDispatchEx派生自IDispatch,除了支援IDispatch所約定的方法之外,還提供了一組擴充方法,用於支援指令碼語言的動態特性,這些動態特性包括:
- 為object新增成員 ("expando") — 使用帶fdexNameEnsure標記的方法 GetDispID.
- 刪除object的成員 — 使用方法DeleteMemberByName 或者DeleteMemberByDispID.
- 支援大小寫敏感的方法 — 使用fdexNameCaseSensitive標記或fdexNameCaseInsensitive標記..
- 使用隱含名搜尋成員 — 使用fdexNameImplicit標記
- 枚舉object所有成員的DISPID — 使用GetNextDispID.
- 通過DispID取得成員的名稱 — 使用GetMemberName.
- 擷取object成員的屬性 — 使用GetMemberProperties.
- 使用執行帶this指標的方法調用 — 使用方法InvokeEx.
- 允許支援命名空間概念的瀏覽器擷取object的命名空間 — 使用方法GetNameSpaceParent.
其實在這裡,IDispatchEx介面存在更重要的意義在於,使得我們有途徑在VC++中訪問並類比動態指令碼語言的特性。下面我們以javascript為例。
舉例:
方法test()中的javascript代碼執行以下動作:
調用Object的constructor(構造器)建立一個新的object,用變數Obj儲存。
在object中建立一個新的成員Elem,並將Elem的值設為方法cat的指標。
調用Elem方法,傳入this指標。因為Elem是Obj的成員,所以this指標就指向Obj。於是方法cat中的代碼也就是為Obj設定了一個新的成員Bar,並賦值為10。
完整的HTML代碼如下:
<HTML>
<BODY>
<SCRIPT LANGUAGE="javascript">
function cat()
{
//建立一個新的成員,並賦值10
this.Bar = 10;
}
function test()
{
// 建立一個新的object
Obj = new Object();
// 建立新的成員,並賦值為cat
Obj.Elem = cat;
//調用Elem("this" == Obj)
Obj.Elem();
//現在Obj.Bar已經存在了
}
-
//
test();
</SCRIPT>
</BODY>
</HTML>
-
我們通過一個控制項用VC++封裝方法test中的代碼。當一個控制項放在一個網頁中,就可以取得瀏覽器中的指令碼引擎的IDispatch介面和IDispatchEx介面( 詳情見上一篇)。
<HTML>
<BODY>
<SCRIPT LANGUAGE="javascript">
function cat()
{
// 建立一個新的成員,並賦值10
-
this.Bar = 10;
}
</SCRIPT>
<OBJECT ID="test" <CLASSID="CLSID:9417DB5D-FA2A-11D0-8CB3-00C04FC2B085">>
</OBJECT>
</BODY>
</HTML>
-
在控制項test中我們將進行如下操作:
-
1、使用GetDispID()取得方法cat的指標。
2、使用GetDispID()取得方法Object的指標。
3、使用InvokeEx()調用方法Object建立一個新的object。
4、使用GetDispID()為object建立一個新的成員Elem。
5、使用InvokeEx()將方法cat的指標賦給成員Elem。
6、使用InvokeEx()調用Elem(也就是方法cat),將object對象作為this指標傳入。於是方法cat為this指標(也就是object)建立一個新成員Bar。
7、使用GetNextDispID()枚舉object,驗證剛才建立的成員Bar是否存在。
-
控制項test中的代碼如下:
- BOOL test(IDispatchEx *pdexScript)
- {
- HRESULT hr;
- VARIANT var;
- DISPID dispid, putid;
- BOOL retval = FALSE;
- BSTR bstrName = NULL;
- IDispatch *pdispObj = NULL, *pdispCat = NULL;
- IDispatchEx *pdexObj = NULL;
- DISPPARAMS dispparams, dispparamsNoArgs = {NULL, NULL, 0, 0};
- // Get dispatch pointer for "cat"
- bstrName = SysAllocString(OLESTR("cat"));
- if (bstrName == NULL) goto LDone;
- hr = pdexScript->GetDispID(bstrName, 0, &dispid);
- if (FAILED(hr)) goto LDone;
- SysFreeString(bstrName);
- bstrName = NULL;
- hr = pdexScript->InvokeEx(dispid, LOCALE_USER_DEFAULT,
- DISPATCH_PROPERTYGET, &dispparamsNoArgs,
- &var, NULL, NULL);
- if (FAILED(hr)) goto LDone;
- pdispCat = var.pdispVal;
- // Create object by calling "Object" constructor
- bstrName = SysAllocString(OLESTR("Object"));
- if (NULL == bstrName) goto LDone;
- hr = pdexScript->GetDispID(bstrName, 0, &dispid);
- if (FAILED(hr)) goto LDone;
- SysFreeString(bstrName);
- bstrName = NULL;
- hr = pdexScript->InvokeEx(dispid, LOCALE_USER_DEFAULT,
- DISPATCH_CONSTRUCT, &dispparamsNoArgs,
- &var, NULL, NULL);
- if (FAILED(hr)) goto LDone;
- pdispObj = var.pdispVal;
- hr = pdispObj->QueryInterface(IID_IDispatchEx, (void **)&pdexObj);
- if (FAILED(hr)) goto LDone;
- // Create new element in object
- bstrName = SysAllocString(OLESTR("Elem"));
- if (NULL == bstrName) goto LDone;
- hr = pdexObj->GetDispID(bstrName, fdexNameEnsure, &dispid);
- if (FAILED(hr)) goto LDone;
- SysFreeString(bstrName);
- bstrName = NULL;
- // Assign "cat" dispatch pointer to element
- putid = DISPID_PROPERTYPUT;
- var.vt = VT_DISPATCH;
- var.pdispVal = pdispCat;
- dispparams.rgvarg = &var;
- dispparams.rgdispidNamedArgs = &putid;
- dispparams.cArgs = 1;
- dispparams.cNamedArgs = 1;
- hr = pdexObj->InvokeEx(dispid, LOCALE_USER_DEFAULT,
- DISPATCH_PROPERTYPUTREF, &dispparams,
- NULL, NULL, NULL);
- if (FAILED(hr)) goto LDone;
- // Invoke method with "this" pointer
- putid = DISPID_THIS;
- var.vt = VT_DISPATCH;
- var.pdispVal = pdispObj;
- dispparams.rgvarg = &var;
- dispparams.rgdispidNamedArgs = &putid;
- dispparams.cArgs = 1;
- dispparams.cNamedArgs = 1;
- hr = pdexObj->InvokeEx(dispid, LOCALE_USER_DEFAULT,
- DISPATCH_METHOD, &dispparams,
- NULL, NULL, NULL);
- if (FAILED(hr)) goto LDone;
- // Confirm that new element "Bar" is in object
- hr = pdexObj->GetNextDispID(fdexEnumAll, DISPID_STARTENUM, &dispid);
- while (hr == NOERROR)
- {
- hr = pdexObj->GetMemberName(dispid, &bstrName);
- if (FAILED(hr)) goto LDone;
- retval = !wcscmp(bstrName, OLESTR("Bar"));
- SysFreeString(bstrName);
- bstrName = NULL;
- if (retval) goto LDone;
- hr = pdexObj->GetNextDispID(fdexEnumAll, dispid, &dispid);
- }
- LDone:
- SysFreeString(bstrName);
- if (pdispCat != NULL)
- pdispCat->Release();
- if (pdispObj != NULL)
- pdispObj->Release();
- if (pdexObj != NULL)
- pdexObj->Release();
- return retval;
- }