VBScript 检测网络连接是否正常

某次维护测试需要配置脚本检测网络是否连通,正常手动检测网络是否连通有两个最为常见的办法:一是ping一个主机;二是打开某知名网站。所以脚本检测也顺着这两个思路进行。

关于ping的VBScript实现有如下代码可供参考:

Function Ping(strTarget)
  Dim objShell, objExec, strPingResults
  Set objShell = CreateObject("WScript.Shell")
  Set objExec = objShell.Exec("ping -n 2 -w 1000 " & strTarget)
  strPingResults = LCase(objExec.StdOut.ReadAll)
  If InStr(strPingResults, "reply from") Then 
    'WScript.Echo VbCrLf & strTarget & " responded to ping."
    Ping = True
  Else
    'WScript.Echo VbCrLf & strTarget & " did not respond to ping."
    Ping = False
  End If
  Set objExec = Nothing
  Set objShell = Nothing
End Function

继续阅读

WScript/VBScript命令行参数使用srvany.exe创建自定义服务

之前写过一篇文章介绍srvany.exe这个小工具的,今天有网友反映能否使用命令行方式创建基于srvany.exe的Windows服务,我想到之前编写的SrvanyUI软件没有命令参数直接调用的功能,本来想完善一下SrvanyUI这个软件的,苦于最近较忙,于是先用VBScript凑合写着一个命令行控制的脚本srvany.vbs,其中用到了RestartService子过程来自Rob van der Woude,在此表示感谢!

基本的思路很简单,就是先创建srvany.exe为可执行映像的NT系统服务,然后在利用注册表修改其间的配置参数,最后再启动服务。

先把srvany.vbs的代码贴在这儿,具体的使用方法稍后附上。

继续阅读

WScript/VBScript脚本全兼容打开文件选择对话框(VBS Open File Dialog)

前面有一篇文章《WScript脚本打开文件夹选择对话框》向大家介绍如何通过VBS打开文件夹选择对话框,其中用到了Shell.Application,这个组件一般Windows系统都自带,所以在兼容方面不需要我们操心。打开/保存文件对话框我们可能在脚本编程中用得到,本来以为打开文件和打开文件夹一样简单,没想到费了一番周折。

起初使用了UserAccounts.CommonDialog组件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
' dir is the initial directory; if no directory is
' specified "Desktop" is used.
' filter is the file type filter; format "File type description|*.ext"
Function GetOpenFileName(dir, filter)
    Dim objDialog
    Set objDialog = WSH.CreateObject("UserAccounts.CommonDialog")
    If VarType(dir) <> vbString Or dir="" Then
        objDialog.InitialDir = _
            CreateObject( "WScript.Shell" ).SpecialFolders( "Desktop" )
    Else
        objDialog.InitialDir = dir
    End If
 
    If VarType(filter) <> vbString Or filter="" Then
        objDialog.Filter = "All files|*.*"
    Else
        objDialog.Filter = filter
    End If
 
    If objDialog.ShowOpen Then
        GetOpenFileName = objDialog.FileName
    Else
        GetOpenFileName = ""
    End If
    Set objDialog = Nothing
End Function
 
' Test
Dim strFileName
strFileName = GetOpenFileName("C:\","All files|*.*|Microsoft Word|*.doc")

继续阅读

WScript/VBScript以命令行的方式通过飞信发送短信或短消息

之前那篇文章《ASP/VBScript通过飞信实现发送短信和短消息功能(Fetion SMS)》简单介绍了我所实现的通过WAP飞信发送短信或者短消息的类FetionMessager,文末也简单给出了使用方式,当然大家可能觉得不够过瘾,所以我在这里将在WSH(Windows Scripting Host)的脚本宿主环境下以CScript.exe命令启动的方式实现命令行发送短信,VBScript或者JScript脚本在本机状态下都可以以两种方式运行,一种是WScript.exe的方式,如果不主动调用MsgBox或者其他显示窗体的函数或者对象,是不会有窗体产生的;另外一种是CScript.exe,这种就是控制台命令行模式了,会有黑黑的命令提示符窗口产生,当然在批量操作以及带有参数开关操作的模式下,这种方式明显要方便得多。

好了,关于完整的VBS发飞信短信的脚本代码我已经托管到GitHub上了,大家可以访问这里获取。下面简单介绍一下调用参数。

最简单的调用方式无非是在命令提示符状态下,输入CScript fetion.vbs调用,当然我们可以利用CScript //NoLogo fetion.vbs调用,这样可以不用显示微软的Logo文字,感觉会清爽一些,下面我将介绍一下调用参数,其实你直接调用的话,脚本已经自动提示调用参数了,我在这里在说明一下:

继续阅读

WScript/VBScript 获取脚本所在文件夹及任意文件所在目录路径

Windows脚本宿主(Windows Scripting Host或WSH)确实给我们编写本地脚本带来了方便,但是如何获取脚本文件自身目录或者是任意文件所在的目录路径呢?在Win32编程中我们可以通过PathRemoveFileSpec这样的Path*系列API,来对文件路径进行处理,当然这里是脚本,正常情况下没有访问Win32 API的权限,不过也有变通的办法来实现类似的功能。

下面我以获取脚本所在文件夹目录路径为例,讲解一下如何处理这类问题,WSH为我们提供了一个环境变量,名称为WScript.ScriptFullName或者写成WSH.ScriptFullName,通过直接输出变量,我们很容易得到脚本的全路径,当然这个是包含脚本名称的,类似于C:\ScriptsFolder\Sample.vbs这样的路径,如何得到C:\ScriptsFolder\呢?比如我们要向这个目录下写入配置文件,大家肯定会想到,我们去掉Sample.vbs不就可以了,是的,这是个办法,当然去掉的办法有很多,比如说基本的InStrRev函数,查找\斜杠,然后再Left截取字符串,这个思路就形成了我们第一个函数PathRemoveFileSpec1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function PathRemoveFileSpec1(strFileName)
  ' 将类Unix路径 / 替换为 \
  strFileName = Replace(strFileName, "/", "\")
 
  Dim iPos
  ' 从路径末尾开始搜索\,这样从第一个字符到这个位置
  ' 就是我们所需要的
  iPos = InStrRev(strFileName, "\")
  ' 使用Left保留需要的部分
  PathRemoveFileSpec1 = Left(strFileName, iPos)
End Function
 
' 测试输出
WScript.Echo PathRemoveFileSpec1(WScript.ScriptFullName)

继续阅读

VBScript利用WScript.Shell创建快捷方式

有时候利用了COM组件的脚本可以变得很强大,比如我们可以用它实现所有需要编程编译为本机代码(*.EXE)才能实现的功能。今天要介绍的就是如何让VBScript脚本利用WScript.Shell组件在桌面上创建快捷方式。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Option Explicit
 
Dim wshShell, Shortcut
Dim strDir, strName
strDir = "D:\"
strName = "命令提示符"
 
Set wshShell = WSH.CreateObject("WScript.Shell")
  Set Shortcut = wshShell.CreateShortcut(strDir &_
    "\" & strName & ".lnk")
    Shortcut.TargetPath = "C:\Windows\System32\cmd.exe"
    Shortcut.WindowStyle = 1
    Shortcut.Hotkey = "CTRL+ALT+U"
    Shortcut.Description = "这是命令提示符程序描述"
    Shortcut.WorkingDirectory = strDir
    Shortcut.Save
  Set Shortcut = Nothing
Set wshShell = Nothing

运行上述代码后,你将会发现D盘的根目录下建立了一个命令提示符的快捷方式,简单解释一下上面的代码,首先strDir保存的是创建快捷方式的目录路径,strName保存的是快捷方式的名称,大家会发现在稍后的代码中,利用了WScript.Shell的CreateShortcut方法创建了以strDir + strName + “.lnk”为参数的快捷方式,注意这里快捷方式都是以lnk为扩展名的,不过单单是利用CreateShortcut方法还不能创建,我们还要进行快捷方式的配置,然后调用Save方法才能真正保存并创建一个快捷方式,大家可以对任意的快捷方式右击,查看其属性,就可以知道是哪些配置了,这里的一些配置有TargetPath指向路径、WindowStyle窗口模式、HotKey快捷热键、Description鼠标放上去出现的描述提示、WorkingDirectory工作路径等等,关于WindowStyle的值,可以参考《WindowStyle Property》

到这里大家可能会发觉在D盘创建快捷方式貌似意义不大,比如我们想将快捷方式创建到桌面上,那么该怎么办呢?看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Option Explicit
 
Dim wshShell, Shortcut
Dim strDir, strName
strName = "命令提示符"
 
Set wshShell = WSH.CreateObject("WScript.Shell")
  strDir = wshShell.SpecialFolders("Desktop")
  Set Shortcut = wshShell.CreateShortcut(strDir &_
    "\" & strName & ".lnk")
    Shortcut.TargetPath = "C:\Windows\System32\cmd.exe"
    Shortcut.WindowStyle = 1
    Shortcut.Hotkey = "CTRL+ALT+U"
    Shortcut.Description = "这是命令提示符程序描述"
    Shortcut.WorkingDirectory = strDir
    Shortcut.Save
  Set Shortcut = Nothing
Set wshShell = Nothing

恩,是的,我们利用了WScript.Shell的另外一个功能,那就是获得特殊目录路径SpecialFolders,我们这里尝试获取的是桌面的路径位置,然后将其赋值给strDir,然后接下来大家都晓得怎么做吧。

利用Scripting.FileSystemObject组件来枚举文件

基本的算法思想就是首先获得文件夹下的文件集合,然后枚举文件,然后再获得文件夹下子文件夹的集合,然后递归枚举,实际操作时为了更好的模块化,使用了回调函数,如果找到文件就自动交给指定函数处理,我们约定函数的格式如下:

FUNCTION(FileSystemObject, FileObject, Parameter) RETURN AS BOOL

首先第一个参数是FileSystemObject,就是将创建好的Scripting.FileSystemObject对象传入函数让我们加以利用。
其次第二个参数是FileObject,也就是枚举出的单独文件对象,我们可以通过这个对象获得文件一系列的属性和方法。
最后一个参数是Parameter,是在调用枚举时传入的参数,没有特殊需要可以忽略。
返回值,如果返回TRUE则中止枚举,否则将继续枚举直到枚举结束。

VBScript实现如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Class FileOperation
 
    Private AxFile   
    Private Sub Class_Initialize()
        Set AxFile = WSH.CreateObject("Scripting.FileSystemObject")
    End Sub
 
    Private Sub Class_Terminate()
        Set AxFile = Nothing
    End Sub
 
    Private Function GetSubFolders(strFolder)
        If AxFile.FolderExists(strFolder) Then
            Dim oFolders
            Set oFolders = AxFile.GetFolder(strFolder)
            Set GetSubFolders = oFolders.SubFolders
            Set oFolders = Nothing
        Else
            Set GetSubFolders = Nothing
        End If
    End Function
 
    Private Function GetSubFiles(strFolder)
        If AxFile.FolderExists(strFolder) Then
            Dim oFolders
            Set oFolders = AxFile.GetFolder(strFolder)
            Set GetSubFiles = oFolders.Files
            Set oFolders = Nothing
        Else
            Set GetSubFiles = Null
        End If
    End Function
 
    Public Function EnumFiles(strFolder, fCallBackName, Param)
        EnumFiles = True
        If Not AxFile.FolderExists(strFolder) Then
            EnumFiles = False
            Exit Function
        End If
 
        Dim fCallBack
        Dim SubFiles, SubFile, SubFolders, SubFolder
 
        Set fCallBack = GetRef(fCallBackName)
 
        Set SubFiles = GetSubFiles(strFolder)
        For Each SubFile In SubFiles
            If fCallBack(AxFile, SubFile, Param) Then Exit For
        Next
        Set SubFiles = Nothing
 
        Set SubFolders = GetSubFolders(strFolder)
        For Each SubFolder In SubFolders
            Call EnumFiles(AxFile.GetAbsolutePathName(SubFolder), _
                                  fCallBackName, Param)
        Next
        Set SubFolders = Nothing
 
        Set fCallBack = Nothing
    End Function
 
End Class

貌似VBScript没有函数指针的概念,在查阅了脚本手册后找到GetRef函数,用以根据字符串格式的函数名获取函数的引用,虽然看上去比较别扭,但是还是可以实现函数指针这个功能的。

JavaScript/JScript实现如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function FileOperation() {
    this.self = WSH.CreateObject("Scripting.FileSystemObject");
 
    this.getSubFolders = function(fdr) {
        if (!this.self.FolderExists(fdr))
            return null;
        return (this.self.GetFolder(fdr).SubFolders);
    }
 
    this.getSubFiles = function(fdr) {
        if (!this.self.FolderExists(fdr))
            return null;
        return (this.self.GetFolder(fdr).Files);
    }
 
    this.enumFiles = function(fdr, callback, param) {
        if (!this.self.FolderExists(fdr))
            return false;
        var SubFiles = new Enumerator(this.getSubFiles(fdr));
        for (; !SubFiles.atEnd(); SubFiles.moveNext())
        {
            if (callback(this.self, SubFiles.item(), param))
                return true;
        }
        var SubFolders = new Enumerator(this.getSubFolders(fdr));
        for (; !SubFolders.atEnd(); SubFolders.moveNext()) {
            this.enumFiles(
            this.self.GetAbsolutePathName(SubFolders.item()),
            callback, param);
        }
        return true;
    }
}

JavaScript/JScript的将函数名作为变量传入很方便的实现了函数指针的功能,在这点上JavaScript/JScript的灵活性还是略胜一筹的,不过其foreach功能就没有VBScript那么好用了。

2012年3月21日更新

对VBScript类做了修改,增加了文件夹枚举搜索,增加递归开关,并且为了保证调用灵活,修改了回调原型函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
' Author : wangye
' //wangye.org
Class FileOperation
 
    Private AxFile   
    Private Sub Class_Initialize()
        Set AxFile = WSH.CreateObject("Scripting.FileSystemObject")
    End Sub
 
    Private Sub Class_Terminate()
        Set AxFile = Nothing
    End Sub
 
    Private Function GetSubFolders(strFolder)
        If AxFile.FolderExists(strFolder) Then
            Dim oFolders
            Set oFolders = AxFile.GetFolder(strFolder)
            Set GetSubFolders = oFolders.SubFolders
            Set oFolders = Nothing
        Else
            Set GetSubFolders = Nothing
        End If
    End Function
 
    Private Function GetSubFiles(strFolder)
        If AxFile.FolderExists(strFolder) Then
            Dim oFolders
            Set oFolders = AxFile.GetFolder(strFolder)
            Set GetSubFiles = oFolders.Files
            Set oFolders = Nothing
        Else
            Set GetSubFiles = Null
        End If
    End Function
 
    ' EnumFiles 枚举指定文件夹文件
    '
    ' 回调函数原型为 CallBack(self, fso, file, param)
    '     self  Class FileOperation实例化对象
    '     fso   Scripting.FileSystemObject对象
    '     file  枚举到的文件对象
    '     param 传递给回调函数的参数(如果需要)
    '
    ' strFolder     要枚举的文件所在的文件夹
    ' fCallBackName 列出的每个文件所调用的回调函数名称
    ' Recursion     是否递归搜索
    ' Param         传递给回调函数的参数(如果需要)
    Public Function EnumFiles(strFolder, fCallBackName, Recursion, Param)
        EnumFiles = True
        If Not AxFile.FolderExists(strFolder) Then
            EnumFiles = False
            Exit Function
        End If
 
        Dim fCallBack
        Dim SubFiles, SubFile, SubFolders, SubFolder
 
        Set fCallBack = GetRef(fCallBackName)
 
        If TypeName(strFolder) = "Folder" Then
            Set SubFiles = strFolder.Files
        Else
            Set SubFiles = GetSubFiles(strFolder)
        End If
        For Each SubFile In SubFiles
            If fCallBack(Me, AxFile, SubFile, Param) Then Exit For
        Next
        Set SubFiles = Nothing
 
        If Recursion Then
        Set SubFolders = GetSubFolders(strFolder)
            For Each SubFolder In SubFolders
                Call EnumFiles(AxFile.GetAbsolutePathName(SubFolder), _
                          fCallBackName, Recursion, Param)
            Next
            Set SubFolders = Nothing
        End If
 
        Set fCallBack = Nothing
    End Function
 
    ' EnumFolders 枚举指定文件夹
    '
    ' 回调函数原型为 CallBack(self, fso, folder, param)
    '     self  Class FileOperation实例化对象
    '     fso   Scripting.FileSystemObject对象
    '     folder  枚举到的文件夹对象
    '     param 传递给回调函数的参数(如果需要)
    '
    ' strFolder     要枚举的文件夹所在的文件夹
    ' fCallBackName 列出的每个文件夹所调用的回调函数名称
    ' Recursion     是否递归搜索
    ' Param         传递给回调函数的参数(如果需要)
    Public Function EnumFolders(strFolder, fCallBackName, Recursion, Param)
        EnumFolders = True
        If Not AxFile.FolderExists(strFolder) Then
            EnumFolders = False
            Exit Function
        End If
 
        Dim fCallBack
        Dim SubFolders, SubFolder, ChildFolders, ChildFolder
 
        Set fCallBack = GetRef(fCallBackName)
        Set SubFolders = GetSubFolders(strFolder)
        For Each SubFolder In SubFolders
            If fCallBack(Me, AxFile, SubFolder, Param) Then Exit For
            If Recursion Then
                Set ChildFolders = SubFolder.SubFolders
                For Each ChildFolder In ChildFolders
                    If fCallBack(Me, AxFile, ChildFolder, Param) Then Exit For
                    Call EnumFolders(AxFile.GetAbsolutePathName(ChildFolder), _
                              fCallBackName, Recursion, Param)
                Next
                Set ChildFolders = Nothing
            End If
        Next
        Set SubFolders = Nothing
        Set fCallBack = Nothing
    End Function
End Class
 
' Example:
' Function ParseFiles(self, fso, file, param)
'     WSH.Echo file.Name & " at " & fso.GetAbsolutePathName(file)
' End Function
' Dim fp
' Set fp = New FileOperation
'     fp.EnumFiles "C:\", "ParseFiles", True, Null
' Set fp = Nothing

采用插件机制的批量文件扫描及进程处理工具

由于平常工作中需要大批量处理文件,所以用蹩脚的JavaScript语言编写了这个脚本,基本实现了文件及进程的枚举扫描,然后可以通过额外插件实现处理相应的文件或者进程,插件放在plugins文件夹里。

我这里写了3个插件,分别是批量打印Word文档、枚举系统活动进程、枚举RMVB、RM、AVI、WMV、MKV格式的电影文件。
[点击这里下载]

批量文件扫描及进程处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function Component(ActiveXObject) {
 
  // 创建对象建议使用ActiveXObject.open(名称);
  // 比如ActiveXObject.open("Word.Application");
 
  this.name = "插件名称"; // 将显示在主界面下拉框中
  this.cancel = false; // 是否取消扫描,运行中设为true会自动中止扫描
  this.success = true; // 是否处理成功
  this.log = new Log(); // 日志对象
 
  // 日志分为3种,将会在列表框反应出来
  // this.log.error("出错消息文本");
  // this.log.info("正常消息文本");
  // this.log.warning("警告消息文本");
 
  this.load = function() {
    // 加载插件时所要运行的代码
  }
 
  this.unload = function() {
    // 卸载插件时所要运行的代码
  }
 
  this.dispatch = function(parentObj, selfObj, msg){
    switch(msg) {
      case "file": // 文件处理分支
      // parentObj.self (注意这里的.self)
      //就是Scripting.FileSystemObject对象
      // selfObj为每个File对象
      break;
      case "process": // 进程处理分支
      // parentObj 就是winmgmts的GetObject对象
      // selfObj为Win32_Process 每个进程对象
      break;
    }
    return false;
  }
}

目前已知的问题
1. 某些系统上双击启动会报错,请关闭IE浏览器并清除进程中多余的iexplore.exe进程后再试,如果错误依旧,请再次手动打开IE浏览器,然后再试。如果问题还是存在,建议关闭其他浏览器。(感谢威言威语提供)
2. 外文计算机上执行可能会出错,建议将脚本内中文改成英文后再运行。(感谢解皞在日文系统下的测试)

如果有什么问题或者建议欢迎提出!

WScript脚本打开文件夹选择对话框

最近在弄一些WScript脚本,有段功能需求就是弹出个打开文件夹的对话框,之前老是在”MSComDlg.CommonDialog“上纠结,后来才发现原来那个只能摆弄出文件选择对话框,后来在查阅了一些资料后才知道,要调用”Shell.Application“这个组件,哎,微软也应该把这些组件组合组合了,要不还真不容易找到。

文件夹浏览对话框

贴出部分代码,后来根据需要我还改写了JavaScript版本,话说JS的对象引用很是方便,不用像VBS那样Set对象了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Const SHELL_MY_COMPUTER = &H11
Const SHELL_WINDOW_HANDLE = 0
Const SHELL_OPTIONS = 0
Function GetOpenDirectory(title)
	Dim ShlApp,ShlFdr,ShlFdrItem
 
	Set ShlApp = WSH.CreateObject("Shell.Application")
	Set ShlFdr = ShlApp.Namespace(SHELL_MY_COMPUTER)
	Set ShlFdrItem = ShlFdr.Self
	GetOpenDirectory = ShlFdrItem.Path
	Set ShlFdrItem = Nothing
	Set ShlFdr = Nothing
 
	Set ShlFdr = ShlApp.BrowseForFolder _
				(SHELL_WINDOW_HANDLE, _
				title, _
				SHELL_OPTIONS, _
				GetOpenDirectory)
	If ShlFdr Is Nothing Then
		GetOpenDirectory = ""
	Else
		Set ShlFdrItem = ShlFdr.Self
		GetOpenDirectory = ShlFdrItem.Path
		Set ShlFdrItem = Nothing
	End If
	Set ShlApp = Nothing
End Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var SHELL_MY_COMPUTER = 0x11;
var SHELL_WINDOW_HANDLE = 0;
var SHELL_OPTIONS = 0;
 
function openDir(title) {
  var shlfdr,
  shlapp = WSH.CreateObject("Shell.Application");
 
  shlfdr = shlapp.BrowseForFolder(
			SHELL_WINDOW_HANDLE,
			title,
			SHELL_OPTIONS,
			shlapp.Namespace(SHELL_MY_COMPUTER).Self.Path);
  if (shlfdr == null)
     return "";
  return shlfdr.Self.Path;
}