Ruby Ruby 如何获取 COM 对象的返回值参数?

alantsui · 2021年08月31日 · 最后由 alantsui 回复于 2021年09月03日 · 544 次阅读

各位大佬,小弟以前搞 Windows 下桌面程序的,刚搞 Ruby 没多久,遇到了这个的问题,一直没找到答案。 问题是:用 C# 的时候,在调用对象方法时,可以通过在参数前加 out,表示这个参数是一个回传值的参数,方法执行后,这个参数的值就是方法传回的值。 这种情况一般都在调用某些 SDK 时,使用得很频繁。 请问,Ruby 怎么实现这种参数?

个人理解,作为动态语言,可能对于 out 没有特别大的需求?一个方法要返回多个值的时候,直接用数组、哈希之类的包起来返回应该就可以了吧,例如:

def foo(a,b) = [a,b]
=> :foo
a,b=foo(1,3)
=> [1, 3]
a
=> 1
b
=> 3

感谢大哥回复,但我的意思是不是这个。 我现在遇到的问题是需调用一个 Win32 的 COM 控件,用 Python 行得通,但是用 Ruby 就真的遇到问题了。 例如,现在需要调用 某个 Win32 COM 控件 my_com 的某个方法 my_method,这个方法在 SDK 中的说明如下:

VARIANT_BOOL my_method 
(LONG   UserID, 
BSTR    *Name, 
BSTR    *Password
)       

这个方法的 input parameter 是 UserID,另外两个带星号的参数是 output parameter 或者说是 reference parameter,也就是返回值。

用 python 很容易解决这个问题:

# 下面的 xxxxx.xxxxx 是 COM 控件对象,例如 Excel.Application 
my_com = win32com.client.Dispatch('xxxxx.xxxxx')  
myUserID = '10001'
# python 只需要这样,方法在调用时不需要写返回值参数,myName, myPassword 直接作为返回值
myUserID, myName, myPassword = my_com.my_method(myUserID)
print(myName)          # "张三"
print(myPassword)    # "888888"

我尝试了 Ruby,按照 ruby-doc 网站http://ruby-doc.com/stdlib-2.6.5/libdoc/win32ole/rdoc/WIN32OLE.html中的例子,效果不理想,获取不到返回值:

require "win32ole"
include WIN32OLE::VARIANT

# 下面的 xxxxx.xxxxx 是 COM 控件对象,例如 Excel.Application 
my_com = WIN32OLE.new('xxxxx.xxxxx')
# 直接调用
myUserID = '10001'
myName = ''
myPassword = ''
p my_com.my_method(myUserID, myName, myPassword)        # 返回 True,证明确实执行了
p WIN32OLE::ARGV      # 返回 ['10001', '', ''],没有获取到返回值
# 换一种方法
myUserID =  WIN32OLE_VARIANT.new('10001', VT_I4)
myName = WIN32OLE_VARIANT.new('', VT_BSTR)
myPassword = WIN32OLE_VARIANT.new('', VT_BSTR)
p mycom.invoke('my_method', myUserID, myName, myPassword)  # 返回 True,证明确实执行了
p myName.value          # 返回 "",仍然没有获取到返回值
p myPassword.value    # 返回 "",仍然没有获取到返回值

alantsui 回复

抱歉,是我浅薄了 😶 曾在 Python 里见过例子那样返回多个值的处理,所以下意识以为 Ruby 也是类似的做法

因为确实不了解,大概帮不上什么忙,唯一有点好奇的就是为何 myUserID 赋值时都使用字符串呢?

因为你的 IDL 中接口方法参数类型是BSTR *,而不是BSTR,这意味着tagVARIANTvt成员是BYREFBSTR的组合。 请参考 SDK 中OAIdl.h对 VARIANT 注释:

/* VARIANT STRUCTURE
    VARTYPE vt;
    ...
    union {
       ...
       ...
      BSTR *         VT_BYREF|VT_BSTR
      ...
    }
*/

试试下面的代码:

myName = WIN32OLE_VARIANT.new("", WIN32OLE::VARIANT::VT_BYREF | WIN32OLE::VARIANT::VT_BSTR)

p.s. 扩展阅读 Black Hat 2009 议题:Attacking Interoperability中的 COM Automation 小节。这篇白皮书虽然是漏洞挖掘方向的,但详细介绍了自动化技术的基础概念,对解决问题可能有一些帮助。

exfx 回复

🙏 🙏 🙏 🙏 🙏 🙏 我已经激动地给大佬跪下了! 这个问题困扰了我 2 周,还把提供 SDK 的供应商工程师一顿臭骂,甚至动摇了我对 Ruby 的信仰。 今天终于解决了!!! 还是自己的知识不够全面,再次谢谢大佬的指点!!!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号