2009-06-08 98 views
1

這很簡單。有一個C++函數使用ByRef參數同時返回三個變量。將傳統ASP VBScript參數ByRef傳遞給COM C++

STDMETHODIMP CReportManager::GetReportAccessRights(long lReportCode, VARIANT_BOOL *bShared, VARIANT_BOOL *bRunOnly, VARIANT_BOOL *bCopy) 

然而,VBScript的ASP代碼似乎並沒有調用C++函數時,拿起新值bShares,bRunOnly和BCOPY。

dim bAllShared, bAllCopy, bAllRunOnly 
bAllShared = true 
bAllCopy = true 
bAllRunOnly = true 
m_oReportManager.GetReportAccessRights CLng(m_lRptCod), CBool(bAllShared), CBool(bAllRunOnly), CBool(bAllCopy) 
'bAllShared always equals true 

有什麼我可以解決這個問題嗎?任何人都可以解釋爲什麼這樣工作?

回答

4

有兩個問題:

首先,你無法檢索從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]參數最初是一個字符串,那麼這個內存將被泄漏;如果它有一個對象,那個對象將永遠不會被銷燬。

+0

真棒回覆!謝謝。 – ssorrrell 2009-06-09 12:55:45

0

在這種情況下實現的另一個糟糕的解決方案是使用VB6來包裝C++函數調用並提供3個引用變量作爲VB6 COM對象的函數。

Option Explicit 
Private bSharedaccess As Boolean 
Private bRunOnlyaccess As Boolean 
Private bCopyaccess As Boolean 

Public Sub Initialize(ByVal oSession As Starbridge.Session, ByVal lReportID As Long) 

    bSharedaccess = True 
    bRunOnlyaccess = False 
    bCopyaccess = True 
    Call oSession.ReportManager.GetReportAccessRights(lReportID, bSharedaccess, bRunOnlyaccess, bCopyaccess) 

End Sub 

Public Function GetSharedAccess() 
    GetSharedAccess = bSharedaccess 
End Function 

Public Function GetRunOnlyAccess() 
    GetRunOnlyAccess = bRunOnlyaccess 
End Function 

Public Function GetCopyAccess() 
    GetCopyAccess = bCopyaccess 
End Function