2009-06-08 107 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