有兩個問題:
首先,你無法檢索從VBScript傳遞迴爲[ref]
參數值,除非他們是在C++代碼VARIANT
類型。
VBScript使用稱爲COM自動化的後期綁定技術,該技術通過單個通用方法調用將每個方法調用路由到COM對象:IDISPATCH:Invoke(...)
。 (Visual Basic使用相同的技術,當你暗淡了可變As Object
,並在其上調用)
Invoke()
需要一個字符串,它是要調用該方法的名稱,參數(加上其他的東西的數組,它是不是這裏很重要)。
您的C++對象不必擔心它,因爲ATL支持稱爲雙接口的東西,它將爲您執行所有討厭的工作。當你的對象接聽電話時IDISPATCH:Invoke()
,ATL將:
- 查找請求的方法名稱,並識別您的類中的相應方法(如果它存在,否則會拋出一個錯誤回來的VBScript)。
- 根據方法的簽名,根據需要將
VARIANT
(技術上VARIANTARG
,幾乎相同)的任何輸入參數轉換爲其適當的數據類型(並且如果它們與您的方法預期不匹配,則會發出錯誤)
- 使用未打包的參數調用您的
GetReportAccessRights()
方法。
當您GetReportAccessRights()
方法返回時,ATL重新打包[retval]
參數到一個新的VARIANT
(techincally VARIANTARG
),並返回到VBScript中。現在
,您可以傳回[ref]
值,但它們必須是VARIANT
秒。 ATL不會爲您重新包裝除[retval]
之外的任何參數值,因此您必須使用VARIANT *
類型的任何[ref]
參數,以便您返回給調用方。當你這樣做時,ATL將保持參數不受干擾,VBScript將正確接收它。
要與變種工作,COM頭爲我們提供了很方便的宏和常量,我將在這裏使用(VT_BOOL,V_VT(),V_BOOL(),失敗()):
// I usually initialize to Empty at the top of the method,
// before anything can go wrong.
VariantInit(bAllShared);
// My bad -- ignore the above. It applies to [out] parameters only.
// Because bAllShared is passed as a [ref] variable,
// calling VariantInit() on them would leak any preexisting value.
// Instead, read the incoming value from the variable (optional),
// then "clear" them before storing new values (mandatory):
// This API figures out what's in the variable and releases it if needed
// * Do nothing on ints, bools, etc.
// * Call pObj->Release() if an Object
// * Call SysFreeString() if a BSTR
// etc
VariantClear(bAllShared);
初始化它們;那會導致他們以前的數值泄漏。
要讀取VARIANT
:
// Always check that the value is of the proper type
if (V_VT(bAllShared) == VT_BOOL) {
// good
bool myArg = (V_BOOL(bAllShared) == VARIANT_TRUE);
} else {
// error, bad input
}
甚至更好,你應該總是嘗試自己進行轉換,因爲VBScript用戶期望「真」與1有同樣的表現爲VARIANT_TRUE。幸運的是,COM具有對於一個真棒工具API:
// This is exactly the same thing that VBScript does internally
// when you call CBool(...)
VARIANT v;
VariantInit(&v);
if(FAILED(VariantChangeType(&v, &bAllShared, 0, VT_BOOL))
{
// error, can't convert
}
bool myArg = (V_BOOL(v) == VARIANT_TRUE);
要寫入VARIANT
:
// Internal working value
bool isShared;
...
// set the Variant's type to VARIANT_BOOL
V_VT(bAllShared) = VT_BOOL;
// set the value
V_BOOL(bAllShared) = (isShared ? VARIANT_TRUE : VARIANT_FALSE);
現在,第二個問題是在你的示例VBScript代碼:
m_oReportManager.GetReportAccessRights _
CLng(m_lRptCod), CBool(bAllShared), CBool(bAllRunOnly), CBool(bAllCopy)
因爲你傳遞的參數是CBool(something)
等,所以你傳回臨時變量(CBool(...)的返回值),而不是實際變量bAllShared
等。即使使用正確的C++實現,返回的值也會被丟棄爲中間值。
你需要調用的方法是這樣的:
m_oReportManager.GetReportAccessRights _
CLng(m_lRptCod), bAllShared, bAllRunOnly, bAllCopy
這是正確的。你不需要「轉換」這些值。無論你做什麼,VBScript將始終通過一個VARIANT
。不用擔心,正如我上面所說的,即使對於bool類型的輸入參數等,ATL也會爲您撥打CBool()
。
(ATL調用CBool將()?這不是一個VBScript函數?是的,但CBool將()是一個簡單的包裝圍繞VariantChangeType()
,而這正是ATL會叫你)
編輯: 我忘了提及別的東西:VBScript不支持[out]
參數;只有[ref]
參數。不要在C++中聲明你的參數爲[out]
。如果您的方法聲明[out]
參數,VBScript將按照[ref]
參數行事。這會導致參數的傳入值被泄漏。如果其中一個[out]參數最初是一個字符串,那麼這個內存將被泄漏;如果它有一個對象,那個對象將永遠不會被銷燬。
真棒回覆!謝謝。 – ssorrrell 2009-06-09 12:55:45