现在的位置: 首页 Script >正文

vbs调用com的一个大坑

调试某COM对象时用vbs调用总是出错,仔细研究才发现原来VBS有这样一个大坑,做个标记

有两个COM接口,姑且称之为IA、IB,定义大体是:

[uuid(....),dual, oleautomation]
interface IA:IDispatch
{
    [id(00000000), propget]
    HRESULT DefaultMethod([out, retval] long* pRetVal);
}
[uuid(....),dual, oleautomation]
interface IB:IDispatch
{
    [id(12345678)]
    HRESULT MethodA([in] IA* pObj,[in] long id,[out, retval] long* pRetVal);
}

对应的ProgId为Test.A、Test.B。现在我想调用Test.B的MethodA方法并获取返回值该怎么办?有人说了可以直接调用啊,然后写了这么个vbs出来:

set obja=createobject("test.a")
set objb=createobject("test.b")
msgbox objb.methoda(obja,1)

看起来没错,但执行后只会返回个0x800A0005无效的过程调用或参数或0x800A01B6对象不支持此属性或方法。

找了很久没找到原因,偶然间把msgbox去掉,然后弹出了个“调用sub不能使用括号”的错误,瞬间来了灵感:

vbs支持不加括号调用一个方法,也就是msgbox 1和msgbox(1)是等价的。msgbox是需要参数的函数,那么调用无参函数的语法就和函数名一样了。

而很不巧,vbs也支持直接将对象当作方法调用,调用的目标就是id为0的方法。

那么问题来了:如果一个对象既有id为0的无参方法,同时又作为参数传到另一个函数中会怎样?答案是vbs会自作聪明的先调用id为0的方法再传参,即objb.methoda(obja,1)第一个参数传进去了obja的返回值,这不是坑爹么。

解决办法就是把这个对象先扔到字典里面,Session也好Scripting.Dictionary也好,然后直接从这个字典里取值传参,注意中间不能用临时变量保存这个对象。代码大致是这样:

set dic=createobject("scripting.dictionary")
dic.add "obj",obja
objb.methoda(dic("obj"),1)

这里又出现个问题:dic.add同样调用了obja,为什么这里就没调用默认参数?答案是dic.add的签名是VARIANT*,直接传了指针进去……

坑爹的vbs,要不是为了asp兼容才不写这东西,真是累心。