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

时间:2022-02-03 01:34:48

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

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

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.

但是,在调用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

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

有什么办法可以解决这个问题吗?任何人都可以解释为什么这样工作?

2 个解决方案

#1


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中检索作为[ref]参数传回的值,除非它们在C ++代码中的类型为VARIANT。

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)

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

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).

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

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:

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

  • 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).
  • 查找所请求的方法名称并在类中标识相应的方法(如果存在,否则它将在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)
  • 根据需要,将VARIANT(技术上为VARIANTARG,几乎相同)的任何输入参数根据方法的签名转换为适当的数据类型(如果它们与您的方法所期望的不匹配,则会抛出错误)

  • Call your GetReportAccessRights() method, with the unpackaged parameters.
  • 使用未打包的参数调用GetReportAccessRights()方法。

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

当您的GetReportAccessRights()方法返回时,ATL将[retval]参数重新打包为新的VARIANT(techincally VARIANTARG)并将其返回给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.

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

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()):

为了使用变体,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); 

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

初始化它们;这将导致他们以前的值泄漏。

To read a VARIANT:

阅读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:

或者甚至更好,你应该总是尝试转换自己,因为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);

To write to a VARIANT:

写入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:

现在,第二个问题出现在您的示例VBScript代码中:

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.

因为你传递的是参数CBool​​(某事物)等,你传回临时变量(CBool​​(...)的返回值),而不是实际变量bAllShared等。即使使用正确的C ++实现,返回的值也是如此将作为中间值丢弃。

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.

那就对了。你不需要“转换”这些值。无论你做什么,VBScript总是会传递一个VARIANT。不用担心,正如我上面所说,即使对于类型为bool等的输入参数,ATL也会为你调用CBool​​()。

(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)

(ATL调用CBool​​()?这不是VBScript函数吗?是的,但是CBool​​()是一个围绕VariantChangeType()的简单包装器,这就是ATL会为你调用的东西)

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.

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

#2


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.

在这种情况下实现的另一个糟糕的解决方案是使用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

#1


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中检索作为[ref]参数传回的值,除非它们在C ++代码中的类型为VARIANT。

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)

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

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).

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

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:

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

  • 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).
  • 查找所请求的方法名称并在类中标识相应的方法(如果存在,否则它将在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)
  • 根据需要,将VARIANT(技术上为VARIANTARG,几乎相同)的任何输入参数根据方法的签名转换为适当的数据类型(如果它们与您的方法所期望的不匹配,则会抛出错误)

  • Call your GetReportAccessRights() method, with the unpackaged parameters.
  • 使用未打包的参数调用GetReportAccessRights()方法。

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

当您的GetReportAccessRights()方法返回时,ATL将[retval]参数重新打包为新的VARIANT(techincally VARIANTARG)并将其返回给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.

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

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()):

为了使用变体,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); 

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

初始化它们;这将导致他们以前的值泄漏。

To read a VARIANT:

阅读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:

或者甚至更好,你应该总是尝试转换自己,因为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);

To write to a VARIANT:

写入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:

现在,第二个问题出现在您的示例VBScript代码中:

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.

因为你传递的是参数CBool​​(某事物)等,你传回临时变量(CBool​​(...)的返回值),而不是实际变量bAllShared等。即使使用正确的C ++实现,返回的值也是如此将作为中间值丢弃。

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.

那就对了。你不需要“转换”这些值。无论你做什么,VBScript总是会传递一个VARIANT。不用担心,正如我上面所说,即使对于类型为bool等的输入参数,ATL也会为你调用CBool​​()。

(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)

(ATL调用CBool​​()?这不是VBScript函数吗?是的,但是CBool​​()是一个围绕VariantChangeType()的简单包装器,这就是ATL会为你调用的东西)

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.

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

#2


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.

在这种情况下实现的另一个糟糕的解决方案是使用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