VBA/VBScript批量搜索多个Word或者Excel指定的包含文字

假设我们有多个Word或者Excel文件,现在我们要从这些文件中搜索特定的关键字,比较笨的办法就是一个一个打开,然后“编辑 – 查找”,文件少还好办,如果文件比较多的话,那么打开这么多文件也累得我们够呛。

下面我讲解一个利用VBScript自动批量搜索特定文字的办法,首先还是上面的算法,只不过将人工一个一个打开换成计算机自动打开。实际上这里主要是枚举文件,然后在利用ActiveX控件调用Word.Application或者Excel.Application内部的查找方法来实现相关功能。

对于一份简单的Word文档,基本的查找VBA可以像下面这样实现:

Dim hasFound ' 定义是否找到
Selection.WholeStory
With Selection.Find
    .ClearFormatting
    .MatchWholeWord = False
    .MatchCase = False
    hasFound = .Execute("要查找的文字")
End With

转化为VBScript代码也很容易,多个创建Word.Application并打开Word文件的过程。

继续阅读

Windows 8仍然支持Visual Basic 6编写的程序

不久前微软宣布Windows 8继续支持Visual Basic 6平台的消息,让VB6再一次接到“死缓”的通知,貌似Windows 7宣布支持VB6时就有消息称,Windows 7之后的平台可能不再支持VB6,看来这次要感谢微软的手下留情,我也不得不怀念一下VB这个编程语言以及其IDE环境Visual Basic 6。

我还记得这个是我接触的第一门计算机编程语言,而且还是上高中信息课上老师教授的,可以说VB算是我编程生涯的引路人吧,但是曾经风光无限的VB6,如今已经风中残烛了,微软也算是亲手缔造也亲自毁灭了她,不由得想起了过去的ASP,一款以VBScript为主的动态网页技术,如今也没落了,就和VB6一样。微软为了其.NET计划真的放弃了太多,但是有一点不得不承认,那就是微软的东西确实上手容易,有利于提高开发效率,所以这也是很多人喜欢微软产品的原因。

继续阅读

VBA/VBScript裁剪拆分Word文档为多个文件(分解为多个独立页面)

标题可能不是很恰当,准确的来说就是将一个有很多页面的Word文档按照指定的页数分解为多个独立的Word文档。本来想通过复制指定页内容然后再新建Word来实现的,后来发现这样做一是很麻烦,二是格式容易错。最后想到了一个比较笨的办法:复制一份原稿,然后除了要保留的页面外,其余的统统删去,然后下个页面再复制一份,以此反复,直到完成全部的拆分。

对于页面的删除,可以通过先选中这个页面内容,然后再执行Selection.Delete方法。这样删除不需要的页面后留下的就是我们需要保留的页面了,这份文档算是完成。

这里有个需要注意的地方,保留Word文档N页中第M页共P页,需要执行两步删除,第一步是删除M页之前的所有页面,第二步是删除M+P页之后的所有页面,然后得到的就是所要的。细节方面就是当执行第一步后,第M页会自动转为第1页(因为前面的都删除了),所以我们接下来执行第二步的时候需要从第1+P页开始,另外如果M=1,说明需要保留从第一页开始,那么第一步可以略去,如果M+P>=总页数,则说明需要保留的包含最后一页,那么第二步就可以略去。

下面提供一份VBScript Class msoWord_SplitPages,其中演示了上面的算法思路,供大家参考:

继续阅读

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")

继续阅读

VB/VBA/VBScript根据出生日期计算年龄函数ComputeAge

有时需要处理一些Excel,这些Excel规定的日期格式是类似2012.02.26这样的形式,当我用到VBA或者VBScript处理这些日期时就很难识别并转换类似的日期格式。一般做法都是通过Split按点对其进行拆分。如果要求计算精确到年的话还好办,直接拿今年的年去减出生年就可以了,比如出生日期是1976.01,那么直接用今年2012 – 1976就得出按年算的年龄,有时可能会要求苛刻一点,比如说要求精确到月,呵呵,再Split,再判断,颇显麻烦,今天终于静下心来搞个统一的函数ComputeAge来处理这些问题,当然要能够识别我目前遇到的形如1972.01、1972.01.02、1972.1.2、72.01、72.01.02、19720102、197201日期格式,计算年龄嘛,我就让这个函数支持精确到日吧(可能用不上)。

单单是计算年龄,可能还不能满足我的胃口,当要统计类似1986年前出生的人的时候,我还要将1986转换一次,感觉麻烦,于是给ComputeAge添加了个比较时间的功能,比较的结果按照标准的-1、0、1进行返回。

比较特别的是这个函数还有个附加的功能就是把形如1972.01、1972.01.02、1972.1.2、72.01、72.01.02、19720102、197201日期格式转换为标准的脚本内置日期变量Date,好啦,说了这么多,函数在这里,用法注释已经写得详细了:

继续阅读

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文字,感觉会清爽一些,下面我将介绍一下调用参数,其实你直接调用的话,脚本已经自动提示调用参数了,我在这里在说明一下:

继续阅读

ASP/VBScript通过飞信实现发送短信和短消息功能(Fetion SMS)

前一段时间正好对电脑发送短信比较感兴趣,因为自己那款老掉牙的手机慢慢的输入文字再群发确实很不方便,于是想移动有没有开放短信接口什么的,上网搜索了有关短信发送的相关知识,找到了Demon的这篇关于《VBS短信飞信发送类(VBSFetion)》,原来是利用了移动的WAP飞信,然后模拟POST,实现短信或者飞信的发送,优点是发送免费,唯一遗憾的是貌似只能发给自己或者飞信好友。不过发送给自己倒是个不错的功能,可以实现一些监控报警类的功能。其实139邮箱那个短信发送到是可以发给任何人,只不过是按正常短信收费标准进行收费,而且主显号码是在你原先的号码上附加了一串长号,这点有点不爽。鉴于Demon在这一方面已经先行一步,我就利用其算法,改造了自己的一个VBScript类实现。

不过值得注意的是,所有POST或者GET发送的消息需要进行编码处理,在ASP中这点还是比较容易的,直接Server.URLEncode就可以了,但是在本机脚本WScript环境下却要费一番周折。网上查找了一下资料,这次仍然找到了Demon的解决方案《用VBS实现PHP的urlencode函数》。好了,基本上解码就分为ANSI版和UTF-8版,我将其改写如下:

继续阅读

ASP/VBScript中CHR(0)的由来以及带来的安全问题

CHR(0)是个特殊的字符,当然在Visual Basic或者VBScript中可以直接用vbNullChar表示,从数值意义上来说这个字符就是数字0。该字符标识着字符串的结束,也称作null-terminated,这个给脚本编程尤其是ASP编程带来了一定的麻烦,很多人可能会问为什么要保留这个特殊字符,我们可以追溯到编写操作系统的语言之一C语言,学过C/C++的童鞋可能知道,在字符串中标识一个字符串结束靠的就是结尾的\0(NULL或者0),否则不能称作为字符串,只能说是字符串数组,任何对于字符串操作的函数如果传入的字符串丢掉了这个结束NULL字符,都有可能会出现异常。

char strbuf[] = "Hello"
// 等价于
char strbuf[] = {'H', 'e', 'l', 'l', 'o', '\0'}

字符串长度的判断函数简单的实现之一:

size_t strlen_a(const char * str) {
  size_t length = 0;
  while (*str++ )
    ++length;
  return length;
}

继续阅读

ASP/WScript/VBScript实现ActiveX COM对象管理器(2)

上面一篇已经介绍了关于对象管理器类cObjectManager,这篇主要讲在ASP中实现持续性对象管理器,大家知道,在ASP技术中,Global.asa是一个特殊的文件,其随着用户访问网站而启用,并在整个会话期内有效,我们可以将cObjectManager字典对象Scripting.Dictionary单独分离到这个文件中,该怎么做才能让所有ASP文件都能共享这一个对象呢?其实我们只需要在Global.asa中应用<object runat=”server”></object>这样的标签就可以创建一个静态的全局对象了。

语法:

1
2
3
4
<object runat="server" scope="scope" id="id"
{progid="progID"|classid="classID"}>
....
</object>

scope 设置对象的作用域(作用范围)(Session 或者 Application)。
id 为对象指定一个唯一的 id。
ProgID 与 ClassID 关联的 id。ProgID 的格式是:[Vendor.]Component[.Version]。ProgID 或 ClassID 必需被指定。
ClassID 为 COM 类对象指定唯一的 id。ProgID 或 ClassID 必需被指定。

global.asa

1
2
<object runat="server" scope="application" id="objMgrDict"
progid="Scripting.Dictionary"></object>

然后我们只需要简单修改一下cObjectManager类:

继续阅读

ASP/WScript/VBScript实现ActiveX COM对象管理器(1)

本文仅实现了一种理论上的管理已经创建ActiveX COM方法,部分代码可以直接使用,由于未进行性能及稳定性测试,所以不建议使用到实际生产环境。下面所述将以VBScript脚本语言为例,同样适用于ASP、VB及VBA(Visual Basic For Application)技术。

大家知道在VBScript中创建对象是通过CreateObject实现的,由于是对象类型,所以必须通过Set关键字进行对象引用,当对象使用完毕后要通过Set [对象名] = Nothing进行对象销毁,这样VBS内部对象引用计数才下降,直至完全销毁回收。

大部分情况下,我们所创建的对象是可以复用的,也就是说,我们在一次使用完毕后可以不用急于销毁对象,然后第二、三次继续使用这个已经存在的对象,这样就避免多次调用CreateObject带来性能上的损耗,以及可能创建失败的风险。

比如说有这样的WScript/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
Function IsFileExists(filename)
  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")
    IsFileExists = fso.FileExists(filename)
  Set fso = Nothing
End Function
 
Function IsFolderExists(filename)
  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")
    IsFolderExists = fso.FolderExists(filename)
  Set fso = Nothing
End Function
 
Sub WriteTextFile(filename, text)
  Dim fso, f
  Set fso = CreateObject("Scripting.FileSystemObject")
    Set f = fso.CreateTextFile(filename, true)
      f.WriteLine text
      f.Close
    Set f = Nothing
  Set fso = Nothing
End Sub
 
If Not IsFileExists("C:\test\data.txt") Then
  If IsFolderExists("C:\test") Then
     WriteTextFile "C:\test\data.txt", "Hello world!"
  End If
End If

这里实现了一个简单的功能,判断文件data.txt,如果不存在,然后判断C:\test文件夹是否存在,存在的话就写入data.txt文件,简单的功能包含了“语法糖”般的函数调用,虽然这样做封装特性比较好,而且提升了我们编码的效率,但是我们这里创建了3次Scripting.FileSystemObject对象,因此程序执行的性能可想而知了,其实较好的做法是创建1次Scripting.FileSystemObject对象,然后FileExists、FolderExists以及CreateTextFile都属于上下文无关的方法,因此可以复用,所以有了下面2种办法:

继续阅读