VBA 笔记

本文记录笔者在开发 VBA 程序中收集到的一些零零碎碎的代码片段以及一些吐槽,另外笔者的 vba 没有系统学习过,如有疏漏欢迎斧正。

  1. VBA 中 Sub 一旦带参数,默认的宏加载器中找不到它了。
    换言之:如果想要从默认宏加载器中运行 Sub 一定不能带参数
    换言之:入口函数不能带参数(笔者总结)

  2. VBA 中 Sub 过程没有返回值,而 Function 过程可以有返回值

  3. VBA 中 引用类型赋值必须要使用 Set 关键字,值类型则不能使用 Set

  4. VBA 中 逻辑中的变量可以不需要声明随处写随时用
    但是不使用 Dim 声明一下,智能感知提示(ctrl+j)中就不会出现哦。
    在 VBA 文件顶部写上 Option Explicit,IDE就会温馨的提醒你忘了声明变量了

    胆敢不写,就是弹窗警告

  1. VBA 中 Function 怎么获取返回值,这点很意外,那就是调用时要传参的必须补全小括号
Set returnvalue = SomeFunction()  '不带参数的也可不加括号'

Set returnvalue = SomeOtherFunction(someparameter) '带参数的就必须带括号,不带括号就代表你不要返回值'

之所以要强调小括号,是因为如果 Function 有返回值的情况下,你加了小括号却不声明个变量来接受它,就会报错:


真相 · 警告

而解决方案也很简单,那就是非要加括号,那你就接受返回值呀:


这下不报错了吧
  1. VBA 中 For Each 居然可以一边遍历一边做删除操作,C# foreach语法表示想都不敢想:
'VBA 中如何遍历文件夹内的文件并依次删除文件,如果删除失败,会报错要求重试'
Sub DeleteFiles()
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
Set distFolder = fs.GetFolder(你的路径)
        On Error GoTo DeletFileError
        For Each fbx In distFolder.Files    
            fs.DeleteFile fbx
        Next
        On Error GoTo 0
Exit Sub
DeletFileError:
        Dim tempFilePath As String: tempFilePath = fbx.path
        MsgBox Prompt:=fbx.Name & " 删除失败,文件被占用,请解除占用后重试!", Title:="请重试"
        If fs.FileExists(tempFilePath) Then Resume Else Resume Next '如果文件存在则尝试继续删除它否则处理下一个文件,避免用户此时手动删除导致的 File Not Found 报错'
End Sub

// 这个没上面用户体验好,遇到报错不会弹出自定义提示。
//https://zhidao.baidu.com/question/1574800320302008660.html
Sub Test()
    Dim fs As Object
    Set fs = CreateObject("scripting.filesystemobject")
    fs.deletefile "D:\Test\temp\*.*"
    fs.deleteFolder "D:\Test\temp\*.*"
End Sub

测试中发现下面无法实现依次遍历并删除文件的功能,因为Set fbx = distFolder.Files(5)跑不通。

                If forceoverride And distFolder.Files.Count Then
                    Dim fbx As File
                    For index = distFolder.Files.Count To 1 Step -1
                        Set fbx = distFolder.Files(5)
                        fs.DeleteFile fbx
                    Next
                End If
  1. VBA 中文件操作要用到 FileSystemObject ,在没有引用 Microsoft.Scripting.Runtime.dll 情况下,使用下面这种方式也是可以使用的:
 Dim fs As Object
 Set fs = CreateObject("Scripting.FileSystemObject")

但是加了这个DLL的引用,就可以直接声明为 FileSystemObject 了,快捷键 ctrl + j 也能出现智能感知提醒了,就这一点为代码编写带来了诸多便利。

 Dim fs As FileSystemObject
 Set fs = CreateObject("Scripting.FileSystemObject")
  1. VBA 中 Error 标签的位置放在哪儿好?
    刚接触 VBA ,对错误处理程序处理很迷:
    a. 不知道错误处理程序放在哪儿,感觉错误标签无处安放总是被无端访问到。
    b. 不知道错误处理程序处理后怎么重试,我就想点重试,不停的重试。
    下面用一个示例来说明白这 2 个问题
Sub SomeSub()
  On Error Goto Err0
    被 Err0 监控的代码
  On Error Goto Err1
    被 Erro1 监控的代码
  On Error GoTo 0
     其他代码 '请注意,On Error GoTo 0 ,表示接下来如果发生报错交给 IDE 弹窗提醒,这是合理的,否则会由上一个错误程序处理报错,必然会输出误导性的告警。'
  Exit Sub '这一句很重要,表示退出 Sub,如果不在此拦截,下面的 Error 标签全都会被执行'
Err0:
    里面写一些友情提示的弹窗,关闭后执行出错语句的接下来的语句
Resume Next
Err1:
  里面写请重试的弹窗,关闭后重试上次出错的操作,区别上一个错误处理程序是 Resume 后少了个 Next
Resume
End Sub

小结:
1. Error 程序放在 End Sub 之上,Exit Sub 之下,所有的 Error 标签都放这里管理
2. Resume 就能实现重试功能了,只要弹窗被点掉,出错时执行的语句会再次被执行,表象为不停提示重试,直至成功处理。
3. 而 Resume Next 则会跳过出错的那个动作去访问下一条语句。类比 C# 中的 Continue 关键字
4. Err 属性中包含了一些报错具体消息/错误码,可以输出供调试

  1. VBA 中 Debug.Print 可以把过程中关心的变量输出到 Immidate 窗口,方便调试

  2. VBA 中 字符串的拼接使用 & 进行。而且符号之间要加空格,你不加IDE都会自动加的那种,但是,空格建议自己加,因为会出现换行时被自动加分号;的情况,动图为证

  3. VBA 中 Sub 内的变量作用域是真的大,一处声明全Sub范围访问无压力,比如下面这段会报错的 C# 代码,VBA 内就不会

    上下文不存在名称“Item”

    反观 VBA 中,声明的临时变量(按C#理解,我就叫他临时变量了)作用域可以延展到 For 循环之外:
    Item Dim 不 Dim ,作用域都一样能延展到 For 循环之外

    得益于变量超级大的作用域,VAB 远隔千里的错误处理程序(Error 标签)中可以输出报错时的上下文信息。比如第 6 条示例中在删除文件报错时,远隔千里的错误处理程序中可以输出文件的名称,告知用户是那个文件异常了。

Tips:VBA 中 怎么在 For 循环中正确的表示数组的长度,不是 arr.Lenght,不是 arr.Count 而是 UBound(arr) 。for循环中取返回值,UBound(arr)的括号一定是不能掉的,这个问题上面已经提到过了。

  1. VBA 中实现按路径逐层依次创建文件夹
'给个路径只要路径节点上的文件夹不存在就逐层创建出来'
Sub hMkDir(fPath As String)
    Dim sp() As String, k%, strP$
    If fPath = "" Then Exit Sub
    strP = IIf(Right(fPath, 1) = "\", Mid(fPath, 1, Len(fPath) - 1), fPath)
    sp = Split(strP, "\"): strP = ""
    Do While k < UBound(sp) + 1
        strP = IIf(strP = "", sp(0), strP & "\" & sp(k))
        If Dir(strP, vbDirectory) = "" Then MkDir strP
        k = k + 1
    Loop
End Sub

这段代码是从网上摘录的,却没想这个广为流传的版本居然还有一个缺陷:

要不然上一步剔除路径最后 “\” 逻辑白写了,sp 数组也冗余一项

  1. VBA 中 怎么使用 Dictionary / 字典的操作
Sub TestDictionaryOperate()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    dict.Add "keyA", "itemA"
    dict.Add "keyB", "itemB"
    dict.Add "keyC", "itemC"
    dict.Add "keyD", "itemD"

   '通过 Key 检查是否存在'
    If dict.Exists("keyA") Then
        MsgBox ("存在此项。")
        dict.Remove ("keyB") 
    End If

   '  读取指定 Key 的值,如果 key 不存在会自动添加一项 value 为 Empty'
    MsgBox (dict.item("keyB"))
   
    Dim item
    For Each item In dict.Items
        Debug.Print item
    Next
    dict.RemoveAll  '移除所有'
    Set dict = Nothing
  End Sub

Tips: 引用 Microsoft Scripting Runtime ,用 Dim dict As Dictionary 替换 Dim dict As Object, Ctrl + J 是不是就有了智能提示了呢?

  1. VBA 中使用正则
'使用正则替换掉字符串中出现的特殊 mark'
 Const markPattern As String = "xxx" '这里是你的正则表达式'
Function RemoveMark(ByVal target As String) As String
    Dim reg As Object
    Set reg = CreateObject("vbscript.regexp")
    With reg
        .Global = True
        .Pattern = Chr(10) & markPattern
        RemoveMark = .Replace(target, "")
    End With
    Set reg = Nothing
End Function

Tips:
1. 引用 Microsoft VBScript Regular Expressions 5.5 ,用 Dim reg As New RegExp 替换 Dim reg As Object,Ctrl + J 可使用智能提示。
2. New 允许隐式创建对象的关键字。如果在声明对象变量时使用New,则在第一次引用该对象时将创建该对象的新实例,因此不必使用Set语句来分配对象引用

  1. VBA 中怎么导出 utf-8 编码的文档
    这个示例曾在 EXCEL 中使用,当时是为了导出 SQL 文件(Sqlite要求文件编码是 utf-8)
Sub GenerateSqliteCommand()
    Dim headCommand As String
    Dim dataSheets As Variant
    Dim sheetName  As Variant
    Dim fullFilePath As String
    Dim outStream As Object
    Dim binStream As Object
    fullFilePath = ThisWorkbook.Path & "\anynameasyouwish.sql"  '在当前这个Excel文档根目录下创建.sql文件.'
    Set outStream = CreateObject("ADODB.Stream")
    outStream.Open
    outStream.Charset = "utf-8"
    outStream.Type = adTypeText
    Set binStream = CreateObject("ADODB.Stream")
    binStream.Open
    binStream.Type = adTypeBinary
    outStream.WriteText ("一些你采集好了的文本数据")
    ' outStream.SaveToFile fileSaveName, 2    如果直接保存文件写入的内容编码为“UTF-8+”而不是“UTF-8”'
    outStream.Position = 3
    outStream.CopyTo binStream
    binStream.SaveToFile fullFilePath, 2     '需要将内容偏移两位去掉“UTF-8+”的bom,fileSaveName为写入数据的文件路径和名字'
    binStream.Close
    outStream.Close
    MsgBox "Sql数据导出完毕!"
End Sub
  1. VBA 中的冒号:
    冒号运算符是 VBA 中的语句分隔符,在笔者看来每读到一个冒号就是声明这是一个新语句
'冒号运算符:是VBA中的语句分隔符'
Public Sub TestMe()
    If 1 = 1 Then: Debug.Print 1    '这句还不如写成下面这句,不写 End IF 不报错仅仅是因为语法特性:写在一行可省略 End IF'
    If 1 = 1 Then  Debug.Print 1 
 
    If 3 = 0 Then:          '冒号运算符对换行的语句不起作用,你可以看到Debug.Print 3会被执行'
    Debug.Print 3
'下面这样写一排冒号也是可行的,IDE会把每一个冒号都执行到 '
'故而笔者觉得冒号不加语句类似换行但不等于空行,估摸着冒号也参与了编译。'
::::::::::::::::::::::::::::
      
'下面示例中,输出结果为 “8c”'
' 解读 IF 后再多的冒号,直到遇见 Else 之前都是属于 If 中的逻辑块,这也是为什么不输出 8a 和 8b 的原因'
    If 8 = 0 Then Debug.Print "8a"::: Debug.Print "8b" Else Debug.Print "8c" 
'同时,如果多个逻辑写在一句,如果没有冒号,则 IDE 报错,这是冒号存在的道理'
End Sub

vba 逻辑写在一句不加冒号 IDE 提示异常

Tips: 冒号运算符在语句很零碎时推荐使用。

  1. VBA 中判等,大于,小于
大于等于 >=
小于等于 <=
不等于 <>
  1. VBA 中如何禁止(UserForm)用户窗体关闭按钮(窗体右上角的小叉)
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = vbFormControlMenu Then Cancel = True
End Sub
  1. VBA 中不能使用常量数组,别人是这么解释的:
    在给常量赋值的表达式中,不能使用变量,用户自定义的函数,或 Visual Basic 的内部函数(如 Chr)。Array属于 Visual Basic 的内部函数,所以不能用Array给常量赋值。
    既然不能声明常量数组,那就申明常量字符串呗,然后再使用的时候 Splite 一下不就行了。
Const AccessString As String = "ElementID|Description|ModifiedTime"
Sub  Example() 
    Dim AccessStringArr() As String
    AccessStringArr = Split(AccessString, "|")
End Sub
    
  1. VBA 中使用 Array初始化数组,数组需要声明为 Variant 而不知特定数值类型
    NG 代码,报错为:Type mismatch
  Dim arr1() As Interior
  arr1 = Array(1, 3, 4)

  Dim arr2() As String
  arr2 = Array("1", "3", "4")

OK 代码

 Dim arr As Variant
  arr = Array("1", "3", "4")

经测试,VBA中以下初始化数组 NG,编译报错:Expected:end of statement

Dim str() = {"1","2","3","4","5"}

查询帮助文档,确认 VBA 中 Array 返回值是一个包含数组的 Variant 实例


写在最后

  1. 本文以佛系模式持续更新。
  2. VBA 用的不多,权当微笔记以备忘,如对读者有帮助也是极好。
  3. 每个条目前都加 VBA 关键字,只是想每一条都能被便捷的搜索到,并不是笔者罗嗦哈~
  4. 笔者 C#-er, VBA 才不是我想要的选择,Emm...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275

推荐阅读更多精彩内容

  • 1.单维数组 1.1 声明数组 声明1个1~50的数组,可以将区间值写出来Dim SZ(1 To 50) As S...
    kupen阅读 708评论 0 1
  • 2018年12月13日 1.字符串截取 适用场景:比如行政区划的 截取 从详细的地址 得到 到 “区”的。(其他...
    silencefun阅读 1,661评论 0 1
  • 1.创建新的工作簿 知识点:Workbook、Worksheet、SaveAs 2.工作簿是否已打开 知识点:Fo...
    kupen阅读 327评论 0 0
  • 1.变量 1.Dim < > As < > 2.规则 变量名称必须使用一个字母作为第一个字符。 ...
    纪同学说阅读 1,068评论 0 2
  • JavaScript,通常缩写为 JS,是一种解释执行的编程语言。它是现在最流行的脚本语言之一。 JavaScri...
    神齐阅读 4,721评论 1 32