1

这很简单。有一个 c++ 函数使用 ByRef 参数同时返回三个变量。

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

但是,调用 c++ 函数时,VBScript ASP 代码似乎没有获取 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

2 回答 2

4

有两个问题:

首先,您不能检索作为[ref]参数从 VBScript 传回的值,除非它们是VARIANTC++ 代码中的类型。

VBScript 使用一种称为 COM 自动化的后期绑定技术,它通过单个通用方法调用将每个方法调用路由到 COM 对象:IDISPATCH:Invoke(...). As Object(当您对变量进行调暗并对其进行调用时,Visual Basic 使用相同的技术)

Invoke()接受一个字符串,它是您正在调用的方法的名称,以及一个参数数组(加上其他在这里不重要的东西)。

您的 C++ 对象不必担心它,因为 ATL 支持称为双接口的东西,它将为您完成所有令人讨厌的工作。当您的对象收到对 的调用时IDISPATCH:Invoke(),ATL 将:

  • 查找请求的方法名称并在您的类中识别相应的方法(如果存在,否则将在 VBScript 中返回错误)。
  • 根据方法的签名,根据需要将任何输入参数从VARIANT(技术上VARIANTARG几乎相同)转换为适当的数据类型(如果它们与您的方法所期望的不匹配,则会抛出错误)
  • GetReportAccessRights()使用未打包的参数调用您的方法。

当您的GetReportAccessRights()方法返回时,ATL 将[retval]参数重新打包成一个新的VARIANT(技术上VARIANTARG)并将其返回给 VBScript。

现在,您也可以传回[ref]值,但它们必须VARIANTs。ATL 不会重新打包除[retval]for 您之外的任何参数值,因此您必须对要返回给调用者VARIANT *的任何参数使用类型。[ref]当您这样做时,ATL 将保持参数不受干扰,而 VBScript 将正确接收它。

为了使用变体,COM 头文件为我们提供了方便的宏和常量,我将在这里使用它们(VT_BOOL、V_VT()、V_BOOL()、FAILED()):

// 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 用户期望“True”和 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

这是正确的。您不需要“转换”这些值。VARIANT不管你做什么,VBScript 总是会通过。不用担心,正如我上面所说,即使对于 bool 类型的输入参数等,ATL 也会CBool()为您调用。

ATL 调用 CBool​​()?这不是一个 VBScript 函数吗?是的,但是 CBool​​() 是一个简单的包装器VariantChangeType(),这就是 ATL 将为您调用的)

编辑: 我忘了提到别的东西:VBScript 不支持[out]参数;只有[ref]参数。不要像[out]在 C++ 中那样声明参数。如果您的方法声明了[out]参数,VBScript 将像[ref]参数一样工作。这将导致参数的传入值被泄露。如果其中一个 [out] 参数最初是字符串,则该内存将被泄漏;如果它有一个对象,则该对象将永远不会被销毁。

于 2009-06-09T00:46:31.180 回答
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
于 2009-06-09T13:05:38.110 回答