很显然,WSH可以利用的对象远远不止这些。本文挂一漏万,谈一些较实用的对象及其用法。 先看一个支持断点续传下载web资源的例子,它用到了上面说的4个常用对象。 复制代码 代码如下: if (lcase(right(wscript.fullname,11))="wscript.exe") then "判断脚本宿主的名称" die("Script host must be CScript.exe.") "脚本宿主不是CScript,于是就die了" end if
if wscript.arguments.count<1 then "至少要有一个参数" die("Usage: cscript webdl.vbs url [filename]") "麻雀虽小五脏俱全,Usage不能忘" end if
url=wscript.arguments(0) "参数数组下标从0开始" if url="" then die("URL can"t be null.") "敢唬我,空url可不行" if wscript.arguments.count>1 then "先判断参数个数是否大于1" filename=wscript.arguments(1) "再访问第二个参数" else "如果没有给出文件名,就从url中获得" t=instrrev(url,"/") "获得最后一个"/"的位置" if t=0 or t=len(url) then die("Can not get filename to save.") "没有"/"或以"/"结尾" filename=right(url,len(url)-t) "获得要保存的文件名" end if if not left(url,7)="http://" then url="http://"&url "如果粗心把“http://”忘了,加上"
set fso=wscript.createobject("Scripting.FileSystemObject") "FSO,ASO,HTTP三个对象一个都不能少" set aso=wscript.createobject("ADODB.Stream") set http=wscript.createobject("Microsoft.XMLHTTP")
if fso.fileexists(filename) then "判断要下载的文件是否已经存在" start=fso.getfile(filename).size "存在,以当前文件大小作为开始位置" else start=0 "不存在,一切从零开始" fso.createtextfile(filename).close "新建文件" end if
for i=1 to 120 "循环等待" if http.readystate=3 then showplan() "状态3表示开始接收数据,显示进度" if http.readystate=4 then exit for "状态4表示数据接受完成" wscript.sleep 500 "等待500ms" next if not http.readystate=4 then die("Timeout.") "1分钟还没下完20k?超时!" if http.status>299 then die("Error: "&http.status&" "&http.statustext) "不是吧,又出错?" if not http.status=206 then die("Server Not Support Partial Content.") "服务器不支持断点续传"
range=http.getresponseheader("Content-Range") "获得http头中的"Content-Range"" if range="" then die("Can not get range.") "没有它就不知道下载完了没有" temp=mid(range,instr(range,"-")+1) "Content-Range是类似123-456/789的样子" current=clng(left(temp,instr(temp,"/")-1)) "123是开始位置,456是结束位置" total=clng(mid(temp,instr(temp,"/")+1)) "789是文件总字节数" if total-current=1 then exit do "结束位置比总大小少1就表示传输完成了" start=start+20480 "否则再下载20k" loop while true
function die(msg) "函数名来自Perl内置函数die" wscript.echo msg "交代遗言^_^" wscript.quit "去见马克思了" end function
function showplan() "显示下载进度" if i mod 3 = 0 then c="/" "简单的动态效果" if i mod 3 = 1 then c="-" if i mod 3 = 2 then c="" wscript.stdout.write chr(13)&"Download ("¤t&") "&c&chr(8)"13号ASCII码是回到行首,8号是退格" end function
WMI的逻辑结构是这样的: 首先是WMI使用者,比如脚本(确切的说是脚本宿主)和其他用到WMI接口的应用程序。由WMI使用者访问CIM对象管理器WinMgmt(即WMI服务),后者再访问CIM(公共信息模型Common Information Model)储存库。静态或动态的信息(对象的属性)就保存在CIM库中,同时还存有对象的方法。一些操作,比如启动一个服务,通过执行对象的方法实现。这实际上是通过COM技术调用了各种dll。最后由dll中封装的API完成请求。
首先是创建对象并连接服务器: 复制代码 代码如下: set objlocator=createobject("wbemscripting.swbemlocator") set objswbemservices=objlocator.connectserver(ipaddress,"rootdefault",username,password)
第一句创建一个服务定位对象,然后第二句用该对象的connectserver方法连接服务器。 除了IP地址、用户名、密码外,还有一个名字空间参数rootdefault。 就像注册表有根键一样,CIM库也是分类的。用面向对象的术语来描述就叫做“名字空间”(Name Space)。 由于RTCS要处理NTLM认证方式和telnet服务端口,所以需要访问注册表。而操作注册表的对象在rootdefault。 复制代码 代码如下: set objinstance=objswbemservices.get("stdregprov") "实例化stdregprov对象" set objmethod=objinstance.methods_("SetDWORDvalue") "SetDWORDvalue方法本身也是对象" set objinparam=objmethod.inparameters.spawninstance_() "实例化输入参数对象" objinparam.hdefkey=&h80000002 "根目录是HKLM,代码80000002(16进制)" objinparam.ssubkeyname="SOFTWAREMicrosoftTelnetServer.0" "设置子键" objinparam.svaluename="NTLM" "设置键值名" objinparam.uvalue=ntlm "设置键值内容,ntlm是变量,由用户输入参数决定" set objoutparam=objinstance.execmethod_("SetDWORDvalue",objinparam) "执行方法"
然后设置端口 复制代码 代码如下: objinparam.svaluename="TelnetPort" objinparam.uvalue=port "port也是由用户输入的参数" set objoutparam=objinstance.execmethod_("SetDWORDvalue",objinparam)
看到这里你是不是觉得有些头大了呢?又是名字空间,又是类的实例化。我在刚开始学习WMI的时候也觉得很不习惯。记得我的初中老师说过,读书要先把书读厚,再把书读薄。读厚是因为加入了自己的想法,读薄是因为把握要领了。 我们现在就把书读薄。上面的代码可以改为: 复制代码 代码如下: set olct=createobject("wbemscripting.swbemlocator") set oreg=olct.connectserver(ip,"rootdefault",user,pass).get("stdregprov") HKLM=&h80000002 out=oreg.setdwordvalue(HKLM,"SOFTWAREMicrosoftTelnetServer.0","NTLM",ntlm) out=oreg.setdwordvalue(HKLM,"SOFTWAREMicrosoftTelnetServer.0","TelnetPort",port)
现在是不是简单多了?
接着是对telnet服务状态的控制。 复制代码 代码如下: set objswbemservices=objlocator.connectserver(ipaddress,"rootcimv2",username,password) set colinstances=objswbemservices.execquery("select * from win32_service where name="tlntsvr"")
这次连接的是rootcimv2名字空间。然后采用wql(sql for WMI)搜索tlntsvr服务。熟悉sql语法的一看就知道是在做什么了。这样得到的是一组Win32_Service实例,虽然where语句决定了该组总是只有一个成员。 为简单起见,假设只要切换服务状态。 复制代码 代码如下:for each objinstance in colinstances if objinstance.started=true then "根据started属性判断服务是否已经启动" intstatus=objinstance.stopservice() "是,调用stopservice停止服务" else intstatus=objinstance.startservice() "否,调用startservice启动服务" end if next
dim wmi "显式定义一个全局变量" set wnd=ie.document.parentwindow "设置wnd为窗口对象" set id=ie.document.all "设置id为document中全部对象的集合" id.confirm.onclick=getref("confirm") "设置点击"确定"按钮时的处理函数" id.cancel.onclick=getref("cancel") "设置点击"取消"按钮时的处理函数"
do while true "由于ie对象支持事件,所以相应的," wscript.sleep 200 "脚本以无限循环来等待各种事件。" loop
sub event_onquit "ie退出事件处理过程" wscript.quit "当ie退出时,脚本也退出" end sub
sub cancel ""取消"事件处理过程" ie.quit "调用ie的quit方法,关闭IE窗口" end sub "随后会触发event_onquit,于是脚本也退出了"
sub confirm ""确定"事件处理过程,这是关键" with id if .ip.value="" then .ip.value="." "空ip值则默认是对本地操作" if not (.app.checked or .sys.checked or .sec.checked) then "app等都是checkbox,通过检测其checked" wnd.alert("至少选择一种日志") "属性,来判断是否被选中。" exit sub end if set lct=createobject("wbemscripting.swbemlocator") "创建服务器定位对象" on error resume next "使脚本宿主忽略非致命错误" set wmi=lct.connectserver(.ip.value,"root/cimv2",.user.value,.pass.value) "连接到root/cimv2名字空间" if err.number then "自己捕捉错误并处理" wnd.alert("连接WMI服务器失败") "这里只是简单的显示“失败”" err.clear on error goto 0 "仍然让脚本宿主处理全部错误" exit sub end if if .app.checked then clearlog "application" "清除每种选中的日志" if .sys.checked then clearlog "system" if .sec.checked then clearlog "security" "注意,在XP下有限制,不能清除安全日志" wnd.alert("日志已清除") end with end sub
sub clearlog(name) wql="select * from Win32_NTEventLogFile where logfilename=""&name&""" set logs=wmi.execquery(wql) "注意,logs的成员不是每条日志," for each l in logs "而是指定日志的文件对象。" if l.cleareventlog() then wnd.alert("清除日志"&name&"时出错!") ie.quit wscript.quit end if next end sub