将经典 ASP VBScript 参数 ByRef 传递到 COM c++

发布于 2024-07-23 19:18:20 字数 584 浏览 4 评论 0原文

这很简单。 有一个 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

我能做些什么来解决这个问题吗? 谁能解释为什么会这样?

It's pretty simple. There's a c++ function that uses ByRef parameters to return three variables at the same time.

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

However, the VBScript ASP code doesn't seem to pick up the new values for bShares, bRunOnly, and bCopy when calling the c++ function.

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

Is there anything I can do to fix this? Can anyone explain why this works this way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一刻暧昧 2024-07-30 19:18:20

有两个问题:

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

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

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

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

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

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

现在,您也可以传回[ref]值,但它们必须VARIANT。 ATL 不会为您重新打包除 [retval] 之外的任何参数值,因此您必须对任何 [ref]VARIANT * 类型code> 您想要返回给调用者的参数。 当您这样做时,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)< /code> 等,您传递回临时变量(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] 参数之一最初是字符串,则该内存将被泄漏; 如果它有一个对象,那么该对象将永远不会被销毁。

There are two problems:

First, you cannot retrieve values passed back as [ref] parameters from VBScript, unless they are of type VARIANT in the C++ code.

VBScript uses a late-binding technology called COM Automation, which routes every method call to COM objects through a single generic method call: IDISPATCH:Invoke(...). (Visual Basic uses the same technology when you Dim a variable As Object and make calls on it)

Invoke() takes a string which is the name of the method you are calling, and an array of parameters (plus other stuff that is not important here).

Your C++ object doesn't have to worry about it because ATL supports something called Dual Interface, which will do all the nasty work for you. When your object receives a call to IDISPATCH:Invoke(), ATL will:

  • Look up the requested method name and Identify the corresponding method in your class (if it exists, otherwise it will throw an error back at VBScript).
  • Translate any input parameters, as needed, from VARIANT (technically VARIANTARG, which is almost identical) to their appropriate data type according to the method's signature (and will throw an error if they don't match what your method expects)
  • Call your GetReportAccessRights() method, with the unpackaged parameters.

When your GetReportAccessRights() method returns, ATL repackages the [retval] parameter into a new VARIANT (techincally VARIANTARG) and returns that to VBScript.

Now, you can pass back [ref] values as well, but they have to be VARIANTs. ATL will not repackage any parameter value other than the [retval] for you, so you have to use a type of VARIANT * for any [ref] argument that you want to return back to the caller. When you do, ATL will leave the parameter undisturbed and VBScript will receive it back correctly.

To work with variants, the COM headers provides us with convenience macros and constants, which I'll use here (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); 

Initialize them; that would cause their previous values to leak.

To read a 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
}

Or even better, you should always try to convert yourself, because VBScript users expect "True" and 1 to behave the same as a VARIANT_TRUE. Fortunately, COM has an awesome utility API for that:

// 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);

To write to a 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);

Now, the second problem is in your sample VBScript code:

m_oReportManager.GetReportAccessRights _
    CLng(m_lRptCod), CBool(bAllShared), CBool(bAllRunOnly), CBool(bAllCopy)

Because you are passing as arguments CBool(something), etc, you are passing back temporary variables (the return value of CBool(...)), not the actual variable bAllShared, etc. Even with the correct C++ implementation, the returned values will be discarded as intermediate values.

You need to call the method like this:

m_oReportManager.GetReportAccessRights _
    CLng(m_lRptCod), bAllShared, bAllRunOnly, bAllCopy

That's right. you don't need to "convert" the values. VBScript will always pass a VARIANT no matter what you do. Don't worry, as I said above, even for input parameters of type bool, etc, ATL will make the call to CBool() for you.

(ATL calls CBool()? Isn't that a VBScript function? Yes, but CBool() is a simple wrapper around VariantChangeType(), and that is what ATL will call for you)

Edit:
I forgot to mention something else: VBScript does NOT support [out] parameters; only [ref] parameters. Do NOT declare your parameters as [out] in C++. If your method declares [out] parameters, VBScript will act like they were [ref] parameters. That will cause the incoming values of the parameters to be leaked. If one of the [out] arguments had originally a string, that memory will be leaked; if it had an object, that object will never be destroyed.

被翻牌 2024-07-30 19:18:20

在本例中实现的另一个糟糕的解决方案是使用 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

Another crummy solution, which was implemented in this case was to use VB6 to wrap the c++ function call and provide the 3 referenced variables as functions of the VB6 COM object.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文