VBS技术内幕:CreateObject函数

标签: , , ,

CreateObject函数可以说是VBS中最强大的函数,没有了它,VBS只能用来算算数学题。我们都知道CreateObject函数可以创建对象,但是很多人并不知道其中的奥秘。

曾经我也不明白为什么在CreateObject函数中传递不同的字符串就可以创建各种各样功能强大的对象。后来无意中看到UMU的《[UMU WSH 教程](9)CreateObject 过程》,才知道CreateObject函数创建的是COM对象,第一个参数是COM对象的ProgID。再后来拜读了Jeff Glatt的《COM in plain C》,知道了如何用纯C语言编写COM组件。

COM(组件对象模型)是一个很复杂的概念,需要用砖头那么厚的书才能讲得清楚,而且没有C++和面向对象编程背景的话很难理解,比较经典的书有《COM原理与应用》、《COM技术内幕》和《COM本质论》,不过貌似都绝版了。

当然,作为VBSer,我们不需要去理解COM的原理或者本质。简单的说,COM就是别人写好的模块,我们要做的仅仅是调用它,而不必关心它的内部实现,这也是COM技术的一个初衷。ProgID可以认为是开发人员为COM对象起的一个名字,我们把COM对象的名字传递给CreateObject函数,告诉它我们想创建这个对象,CreateObject函数就会返回这个对象的指针给你。

例如我可以(当然,你也可以)用VB来编写一个COM组件,然后给它起个名字demon.tw,那么注册该COM组件之后,就可以用CreateObject函数来创建了:

Set blog = CreateObject("demon.tw")
blog.Open '假设我的COM对象实现了Open方法

我们常用的Scripting.FileSystemObject、WScript.Shell、ADODB.Stream等只不过是微软开发的系统自带的COM对象的名字罢了。

那么CreateObject函数是如何创建对象的呢?用OllyDbg跟了一下,核心的代码大概可以分成四步:

CLSIDFromProgIDEx

第一步调用CLSIDFromProgIDEx从ProgID获取对应的CLSID,如果找不到对应的CLSID,就会报错“ActiveX 部件不能创建对象”。

我们可以用注册表编辑器手工查找CLSID。例如要获取WScript.Shell的CLSID,用注册表编辑器查找HKEY_CLASSES_ROOT\WScript.Shell\CLSID的值即可。需要注意的是,《[UMU WSH 教程](9)CreateObject 过程》里说:

1、CreateObject 函数先检查注册表 HKEY_CLASSES_ROOT\WScript.Shell 下的子键 CurVer 的默认值,结果为 WScript.Shell.1,所以知道最新版本是 WScript.Shell.1;

2、读 HKEY_CLASSES_ROOT\WScript.Shell.1,下面有一个子键 CLSID,默认值为 {72C24DD5-D70A-438B-8A42-98424B88AFB8};

这是错误的,CreateObject函数(准确的说是其内部调用的CLSIDFromProgIDEx函数)先检查注册表子键 HKEY_CLASSES_ROOT\WScript.Shell\CLSID是否存在,只要子键存在,即使默认值为空或者不是类标识符,都不会再检查子键CurVer ,只有CLSID子键不存在,才会检查子键 CurVer。

CoGetClassObject

第二步调用CoGetClassObject函数获取IClassFactory接口的指针,如果获取不到,报错“ActiveX 部件不能创建对象”或者“类不支持 Automation 操作”,也可能是其他错误信息,这取决于COM的实现。

CreateInstance

第三步调用IClassFactory接口的CreateInstance方法获取IUnknown接口指针,所有的COM都必须支持IUnknown接口,所以这步应该不会出错。

QueryInterface

最后调用IUnknown接口的QueryInterface方法查询该COM是非支持IDispatch接口,只有支持IDispatch接口的COM类才能用CreateObject创建对象。如果获取到IDispatch接口的指针,就可以给VARIANT变量赋值了;如果不支持IDispatch接口,报错“类不支持 Automation 操作”,也可能是其他错误信息,取决于具体实现。

说了半天还是没有说到一个关键的问题:VBS到底能调用哪些对象?或者说,哪些字符串可以作为CreateObject函数的第一个参数?欲知问题答案,请听下回分解。

随机文章:

  1. VBS调用WMI获取CPU使用率
  2. PHP中的stdClass
  3. 我为什么不用JScript?
  4. 在C语言中嵌入执行VBS
  5. MySQL快速插入大量数据——使用LOAD DATA语句

3 条评论 发表在“VBS技术内幕:CreateObject函数”上

  1. yurenchen说道:

    不错,受益匪浅~

  2. 蓝眸说道:

    如何用VBS脚本运行CRT程序时自动弹出另一个程序,这个脚本怎么写

留下回复