将经典 ASP VBScript 参数 ByRef 传递到 COM c++
这很简单。 有一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有两个问题:
首先,您无法检索从 VBScript 作为
[ref]
参数传回的值,除非它们是 C++ 代码中的VARIANT
类型。VBScript 使用称为 COM Automation 的后期绑定技术,该技术通过单个通用方法调用将每个方法调用路由到 COM 对象:
IDISPATCH:Invoke(...)
。 (当您将变量As Object
调暗并调用它时,Visual Basic 使用相同的技术)Invoke()
接受一个字符串,它是您正在调用的方法的名称,以及参数数组(加上其他在这里不重要的东西)。您的 C++ 对象不必担心它,因为 ATL 支持称为“双接口”的东西,它将为您完成所有讨厌的工作。 当您的对象收到对
IDISPATCH:Invoke()
的调用时,ATL 将: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()):
初始化它们; 这会导致它们之前的值泄漏。
要读取
VARIANT
:或者甚至更好,您应该始终尝试自行转换,因为 VBScript 用户期望“True”和 1 的行为与 VARIANT_TRUE 相同。 幸运的是,COM 有一个很棒的实用程序 API:
要写入
VARIANT
:现在,第二个问题出现在您的示例 VBScript 代码中:
因为您正在作为参数传递
CBool(something)< /code> 等,您传递回临时变量(CBool(...) 的返回值),而不是实际变量
bAllShared
等。即使使用正确的 C++ 实现,返回值将作为中间值被丢弃。您需要这样调用该方法:
没错。 您不需要“转换”这些值。 无论您做什么,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 typeVARIANT
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 variableAs 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: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)GetReportAccessRights()
method, with the unpackaged parameters.When your
GetReportAccessRights()
method returns, ATL repackages the[retval]
parameter into a newVARIANT
(techincallyVARIANTARG
) and returns that to VBScript.Now, you can pass back
[ref]
values as well, but they have to beVARIANT
s. ATL will not repackage any parameter value other than the[retval]
for you, so you have to use a type ofVARIANT *
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()):
Initialize them; that would cause their previous values to leak.
To read a
VARIANT
: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:
To write to a
VARIANT
:Now, the second problem is in your sample VBScript code:
Because you are passing as arguments
CBool(something)
, etc, you are passing back temporary variables (the return value of CBool(...)), not the actual variablebAllShared
, etc. Even with the correct C++ implementation, the returned values will be discarded as intermediate values.You need to call the method like this:
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 toCBool()
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.在本例中实现的另一个糟糕的解决方案是使用 VB6 包装 C++ 函数调用,并提供 3 个引用变量作为 VB6 COM 对象的函数。
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.