曾經(jīng)我也不明白為什么在CreateObject函數(shù)中傳遞不同的字符串就可以創(chuàng)建各種各樣功能強(qiáng)大的對(duì)象。后來(lái)無(wú)意中看到UMU的《[UMU WSH 教程](9)CreateObject 過(guò)程》,才知道CreateObject函數(shù)創(chuàng)建的是COM對(duì)象,第一個(gè)參數(shù)是COM對(duì)象的ProgID。再后來(lái)拜讀了Jeff Glatt的《COM in plain C》,知道了如何用純C語(yǔ)言編寫COM組件。
COM(組件對(duì)象模型)是一個(gè)很復(fù)雜的概念,需要用磚頭那么厚的書才能講得清楚,而且沒(méi)有C++和面向?qū)ο缶幊瘫尘暗脑捄茈y理解,比較經(jīng)典的書有《COM原理與應(yīng)用》、《COM技術(shù)內(nèi)幕》和《COM本質(zhì)論》,不過(guò)貌似都絕版了。
當(dāng)然,作為VBSer,我們不需要去理解COM的原理或者本質(zhì)。簡(jiǎn)單的說(shuō),COM就是別人寫好的模塊,我們要做的僅僅是調(diào)用它,而不必關(guān)心它的內(nèi)部實(shí)現(xiàn),這也是COM技術(shù)的一個(gè)初衷。ProgID可以認(rèn)為是開發(fā)人員為COM對(duì)象起的一個(gè)名字,我們把COM對(duì)象的名字傳遞給CreateObject函數(shù),告訴它我們想創(chuàng)建這個(gè)對(duì)象,CreateObject函數(shù)就會(huì)返回這個(gè)對(duì)象的指針給你。
例如我可以(當(dāng)然,你也可以)用VB來(lái)編寫一個(gè)COM組件,然后給它起個(gè)名字demon.tw,那么注冊(cè)該COM組件之后,就可以用CreateObject函數(shù)來(lái)創(chuàng)建了:
Set blog = CreateObject("demon.tw")
blog.Open '假設(shè)我的COM對(duì)象實(shí)現(xiàn)了Open方法
我們常用的Scripting.FileSystemObject、WScript.Shell、ADODB.Stream等只不過(guò)是微軟開發(fā)的系統(tǒng)自帶的COM對(duì)象的名字罷了。
那么CreateObject函數(shù)是如何創(chuàng)建對(duì)象的呢?用OllyDbg跟了一下,核心的代碼大概可以分成四步:
第一步調(diào)用CLSIDFromProgIDEx從ProgID獲取對(duì)應(yīng)的CLSID,如果找不到對(duì)應(yīng)的CLSID,就會(huì)報(bào)錯(cuò)“ActiveX 部件不能創(chuàng)建對(duì)象”。
我們可以用注冊(cè)表編輯器手工查找CLSID。例如要獲取WScript.Shell的CLSID,用注冊(cè)表編輯器查找HKEY_CLASSES_ROOT\WScript.Shell\CLSID的值即可。需要注意的是,《[UMU WSH 教程](9)CreateObject 過(guò)程》里說(shuō):
1、CreateObject 函數(shù)先檢查注冊(cè)表 HKEY_CLASSES_ROOT\WScript.Shell 下的子鍵 CurVer 的默認(rèn)值,結(jié)果為 WScript.Shell.1,所以知道最新版本是 WScript.Shell.1;
2、讀 HKEY_CLASSES_ROOT\WScript.Shell.1,下面有一個(gè)子鍵 CLSID,默認(rèn)值為 {72C24DD5-D70A-438B-8A42-98424B88AFB8};
這是錯(cuò)誤的,CreateObject函數(shù)(準(zhǔn)確的說(shuō)是其內(nèi)部調(diào)用的CLSIDFromProgIDEx函數(shù))先檢查注冊(cè)表子鍵 HKEY_CLASSES_ROOT\WScript.Shell\CLSID是否存在,只要子鍵存在,即使默認(rèn)值為空或者不是類標(biāo)識(shí)符,都不會(huì)再檢查子鍵CurVer ,只有CLSID子鍵不存在,才會(huì)檢查子鍵 CurVer。
第二步調(diào)用CoGetClassObject函數(shù)獲取IClassFactory接口的指針,如果獲取不到,報(bào)錯(cuò)“ActiveX 部件不能創(chuàng)建對(duì)象”或者“類不支持 Automation 操作”,也可能是其他錯(cuò)誤信息,這取決于COM的實(shí)現(xiàn)。
第三步調(diào)用IClassFactory接口的CreateInstance方法獲取IUnknown接口指針,所有的COM都必須支持IUnknown接口,所以這步應(yīng)該不會(huì)出錯(cuò)。
最后調(diào)用IUnknown接口的QueryInterface方法查詢?cè)揅OM是非支持IDispatch接口,只有支持IDispatch接口的COM類才能用CreateObject創(chuàng)建對(duì)象。如果獲取到IDispatch接口的指針,就可以給VARIANT變量賦值了;如果不支持IDispatch接口,報(bào)錯(cuò)“類不支持 Automation 操作”,也可能是其他錯(cuò)誤信息,取決于具體實(shí)現(xiàn)。
說(shuō)了半天還是沒(méi)有說(shuō)到一個(gè)關(guān)鍵的問(wèn)題:VBS到底能調(diào)用哪些對(duì)象?或者說(shuō),哪些字符串可以作為CreateObject函數(shù)的第一個(gè)參數(shù)?欲知問(wèn)題答案,請(qǐng)聽下回分解。
VBS深入CreateObject函數(shù)
本篇要講的是對(duì)象的創(chuàng)建,屬于 COM 的內(nèi)容,這里不可能說(shuō)太多,大家可以找一些 COM 的書看看,也可以看看 UMU 的其他關(guān)于 COM 的文章:《ATL 體驗(yàn)》、《基于 WebBrowser 的新型應(yīng)用程序研究小記》、《學(xué)習(xí) ATL 的理由》、《關(guān)于 COM 的幾個(gè)概念問(wèn)題》、《關(guān)于 COM 的幾個(gè)概念問(wèn)題(2)》。最常見的對(duì)象有:WScript.Shell、Scripting.FileSystemObject、Scripting.Dictionary 等,這里以 WScript.Shell 為例。
馬上來(lái)看對(duì)象的創(chuàng)建過(guò)程,語(yǔ)句 Set objWSH = CreateObject( "WScript.Shell" ):
1、CreateObject 函數(shù)先檢查注冊(cè)表 HKEY_CLASSES_ROOT\WScript.Shell 下的子鍵 CurVer 的默認(rèn)值,結(jié)果為 WScript.Shell.1,所以知道最新版本是 WScript.Shell.1;
2、讀 HKEY_CLASSES_ROOT\WScript.Shell.1,下面有一個(gè)子鍵 CLSID,默認(rèn)值為 {72C24DD5-D70A-438B-8A42-98424B88AFB8};
3、找到了 HKEY_CLASSES_ROOT\CLSID\{72C24DD5-D70A-438B-8A42-98424B88AFB8},子鍵 InProcServer32 的默認(rèn)值說(shuō)明服務(wù)程序是 C:\WINDOWS\system32\wshom.ocx。
4、對(duì)于腳本可以調(diào)用的 COM 對(duì)象,要使用對(duì)象里的方法 TypeLib 是必要的,HKEY_CLASSES_ROOT\CLSID\{72C24DD5-D70A-438B-8A42-98424B88AFB8} \TypeLib 的默認(rèn)值是 {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B},HKEY_CLASSES_ROOT\TypeLib \{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}\1.0\0\win32 的默認(rèn)值說(shuō)明類型庫(kù)是 C:\WINDOWS\system32\wshom.ocx。
支持腳本調(diào)用的 COM 對(duì)象必然要實(shí)現(xiàn) IDispatch 接口,可以從 C:\WINDOWS\system32\wshom.ocx 的“資源 – TYPELIB”里看出來(lái),每個(gè)對(duì)象開頭的 7 個(gè)函數(shù)都是 QueryInterface、AddRef、Release、GetTypeInfoCount、GetTypeInfo、 GetIDsOfNames、Invoke,前 3 個(gè)是 IUnknown 接口的函數(shù)。PE 文件里的 TYPELIB 資源是 *.idl 源碼文件編譯后的類型庫(kù)的二進(jìn)制數(shù)據(jù),可以反編譯回去。不過(guò) UMU 推薦使用 eXeScope 查看,即使用 eXeScope 打開 C:\WINDOWS\system32\wshom.ocx,查看“資源 – TYPELIB”,可以看出每個(gè)接口函數(shù)的參數(shù)和返回值定義。
VB 開發(fā)環(huán)境就是這樣知道對(duì)象里有什么函數(shù)的。所以,如果我們知道一個(gè)對(duì)象名,卻不知道這個(gè)對(duì)象里有什么函數(shù),可以用上面說(shuō)的方法獲得。
xuejinglan 于 2007年03月31日 星期六 11:40 問(wèn) UMU 這樣一個(gè)問(wèn)題:“系統(tǒng)中存在哪些對(duì)象,對(duì)象有那些函數(shù)可以調(diào)用,如何知道?”這個(gè)問(wèn)題已經(jīng)回答后一半了,下面回答前一半。
對(duì)象的注冊(cè)信息 HKEY_CLASSES_ROOT\CLSID\{GUID} 下可能會(huì)有這樣的一些子鍵:Control 說(shuō)明該組件是一個(gè) ActiveX 控件、Programmable 說(shuō)明該組件支持自動(dòng)化、Insertable 說(shuō)明該組件可以被嵌入到一個(gè) OLE 文檔容器中。能找到 Programmable,說(shuō)明支持自動(dòng)化,也就是支持 IDispatch 接口,所以它可以被腳本語(yǔ)言使用。不過(guò)這種方式比較老了,現(xiàn)在已經(jīng)被一個(gè)的組件類屬代替,即 Implemented Categories 子鍵下面的 GUID 形式的子鍵。比如 HKEY_CLASSES_ROOT\CLSID\{72C24DD5-D70A-438B-8A42-98424B88AFB8}\Implemented Categories\{40FC6ED5-2438-11CF-A3DB-080036F12502},看一下 HKEY_CLASSES_ROOT\Component Categories\{40FC6ED5-2438-11CF-A3DB-080036F12502} 下的 409 字符串值為 Automation Objects,也就是“自動(dòng)化對(duì)象”。
查找“自動(dòng)化對(duì)象”可以使用 VS 帶的工具 oleview.exe,它專門用來(lái)查看 OLE/COM 對(duì)象的注冊(cè)信息,界面如下圖:
人民群眾可能有點(diǎn)頭暈了,總結(jié)一下:組件類屬為 {40FC6ED5-2438-11CF-A3DB-080036F12502}(Automation Objects) 的對(duì)象都支持被腳本調(diào)用。
接下去的創(chuàng)建過(guò)程不屬于腳本應(yīng)該考慮的范圍,有興趣學(xué) COM 的話可以研究研究,很好的一個(gè)機(jī)制,值得學(xué)習(xí)。標(biāo)題: VBS技術(shù)內(nèi)幕:CreateObject函數(shù)
作者: Demon
鏈接: http://demon.tw/reverse/vbscript-internal-createobject.html