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).
- Translate any input parameters, as needed, from
VARIANT
(technicallyVARIANTARG
, 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.
查找所请求的方法名称并在类中标识相应的方法(如果存在,否则它将在VBScript中引发错误)。
根据需要,将VARIANT(技术上为VARIANTARG,几乎相同)的任何输入参数根据方法的签名转换为适当的数据类型(如果它们与您的方法所期望的不匹配,则会抛出错误)
使用未打包的参数调用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 VARIANT
s. 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).
- Translate any input parameters, as needed, from
VARIANT
(technicallyVARIANTARG
, 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.
查找所请求的方法名称并在类中标识相应的方法(如果存在,否则它将在VBScript中引发错误)。
根据需要,将VARIANT(技术上为VARIANTARG,几乎相同)的任何输入参数根据方法的签名转换为适当的数据类型(如果它们与您的方法所期望的不匹配,则会抛出错误)
使用未打包的参数调用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 VARIANT
s. 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