VBA实例详解
VBA中的函数Timer用法
第1.40例 Timer 函数
一、题目:
要求编写一段代码,运用 Timer 函数来计算本代码运行所化的时间。
二、代码:
Sub 示例_1_040()
Dim t, i&, a
t = Timer
For i = 1 To 1000000
a = a + i
Next i
MsgBox Timer - t & "秒" ‘返回0.046875秒
End Sub
三、代码详解
1、Sub 示例_1_040():宏程序的开始语句。宏名为示例_1_040。
2、Dim t, i&, a :声明变量t,a 均为可变型变量,i为长整型变量。
3、t = Timer :把系统的时间赋给变量t。
4、For i = 1 To 1000000 :从1 到 1000000循环。
5、a = a + i :把a+i的值赋给变量a,实际上是一个累加的过程。
6、MsgBox Timer - t & "秒" :利用消息框显示现在的时间减去前面设置的时间,得到运行整个代码所化的时间。
Timer 函数
返回一个 Single,代表从午夜开始到现在经过的秒数。
Timer
说明
Microsoft Windows中,Timer函数返回一秒的小数部分。
出处:http://club.excelhome.net/thread-384395-1-1.html
用VBA计算两个日期之间的工作日(去掉周末两天)
最近公司HR和Finance想算员工的工作天数,想让我帮忙写些VBA,自己从网上找了下代码,自己再改改,以下来自网络。
计算两个日期之间的工作日,用VBA,因量大,最好用数组做
Sub kk() Dim arr, i&, j&, m& arr = Sheet2.Range("b3:f4") For i = 1 To UBound(arr) m = 0 For j = arr(i, 1) To arr(i, 3) If Weekday(j) <> 1 And Weekday(j) <> 7 Then m = m + 1 Next arr(i, 5) = m Next Sheet2.Range("b3").Resize(UBound(arr), 5) = arr End Sub
根据他提供的方法,其实就是判断某个日期是星期一到星期五就日期计数加1,一直到结束,自己改良了下:
Sub m1() For i = 2 To 5000 days = 0 If Range("b" & i) <> "" And Range("c" & i) <> "" Then Dim d1, d2 As Date d1 = Cells(i, "b") d2 = Cells(i, "c") Do While d1 <= d2 If Weekday(d1, vbMonday) < 6 Then days = days + 1 End If d1 = DateAdd("d", 1, d1) Loop Range("d" & i) = days End If Next End Sub
上面的这个方法只算是可以运行,如果计算的天数多并且员工数多,则效果就差了,所以又有了下面的改良。
计算两个日期的整周数,然后乘5,在加上前后不够整周的零头。
Sub m2() For i = 2 To 5000 If Range("b" & i) <> "" And Range("c" & i) <> "" Then Dim d1, d2 As Date d1 = Cells(i, "b") d2 = Cells(i, "c") days1 = 0 days2 = 0 weekcount = 0 Do While Weekday(d1, vbMonday) < 7 And d1 <= d2 If Weekday(d1, vbMonday) < 6 Then days1 = days1 + 1 End If d1 = DateAdd("d", 1, d1) Loop weekcount = DateDiff("w", d1, d2, vbMonday) days2 = Weekday(d2, vbMonday) days2 = IIf(days2 = 6, 5, IIf(days2 = 7, 0, days2)) Range("d" & i) = IIf(d1 >= d2, days1, days1 + 5 * weekcount + days2) End If Next End Sub
以上代码可以通过测试验证效率,如下代码
Sub Button2_Click() d1 = Timer m1 'm2 d2 = Timer MsgBox d2 - d1 End Sub
参考出处:http://www.excelpx.com/thread-299850-1-1.html
VBA记录当前系统时间并精确到毫秒
想做个功能,点一次按钮,就在A1记录一次当前系统时间,要精确到毫秒的。再点一次按钮就在A2显示,以此类推!
例如:这个功能可以用来做歌词记时间!
Sub ttt() ActiveCell.Select tt = Timer h = Int(tt / 3600) m = Int((tt - 3600 * h) / 60) s = Int(tt - h * 3600 - m * 60) ss = Left(tt - Int(tt), 4) Selection.NumberFormatLocal = "yyyy-mm-dd hh:mm:ss.000" Selection.Value = h & ":" & m & ":" & s & ss ActiveCell.Offset(1, 0).Select End Sub
上面的代码确实可以实现这个功能,可是还有个问题,当超过一个屏幕的时候,点着点着,按钮点不到了,excel视图随着点击按钮新的内容屏幕往下移了,按钮看不到了,你不能改变光标位置。所以我再次改良如下代码:
Sub testTime() Dim tt, n tt = Timer n = Range("A65536").End(xlUp).Row h = Int(tt / 3600) m = Int((tt - 3600 * h) / 60) s = Int(tt - h * 3600 - m * 60) ss = Left(tt - Int(tt), 4) Cells(n + 1, 1).NumberFormatLocal = "hh:mm:ss.000" Cells(n + 1, 1) = h & ":" & m & ":" & s & ss End Sub
这个方法可以完美的记录歌词的时间了。
参考出处:http://www.excelpx.com/thread-329989-1-1.html
调试VBA程序常用方法
在中断模式下(ctrl+Break键),可以做:
1.执行 工具----选项----编辑器----勾选“自动显示数据提示”
则当用鼠标悬停在变量或表达式上时,会出现提示窗口,显示其名称和值!
2.在监视窗口查看某些表达式的值,方法是:先用鼠标拖黑以选中表达式,再执行 调试----添加监视,出现“添加监视”对话框。
3.在代码窗口中选中表达式----按shift+F9----出现”快速监视“对话框,显示该表达式的上下文,表达式及其值。
4.按ctrl+G,出现"立即窗口”。它主要有4个作用:
(1).打印信息
在窗口中输入Debug.Print 表达式1;表达式2......,即可以显示各表达式的值!
2.可以给变量或属性赋值!
例如:Form1.BackColor=255
MaxRows=50
3.可以测试过程
在此窗口中可以直接输入过程名称和实参以便调用过程。
例如:
MySub 2*i
4.可以检查错误号
例如:输入
Error 11
按下回车键时,在此窗口中显示11号错误的信息。我常用这个方法来检查错误号!
断点
在设计和中断模式下都可以设置断点,当程序执行到断点语句时,则中断执行,这时处于中断模式!
1.设置断点
在代码窗口,用鼠标点击左边区使之出现黑点,代表已经设置断点!也可以把光标移动到选定语句上,按F9.
断点语句以粗体字表示,颜色可以由: 工具---选项---编辑器格式---代码颜色----“断点点文本”选项设定!
2.撤销断点:
调试----清除所有断点(ctrl+shift+F9)
3.使用Stop语句
关闭项目时,断点不被保存,下次调试时需要重新设置。
如果多次调试,最好的方法是使用Stop语句,每当遇到它,程序进入中断模式。
Stop语句可以保存在代码中,测试完后可以全部取消!
监视窗口可以监视表达式,“快速监视”窗口也可以显示表达式的值。
立即窗口对表达式求值,给变量和属性赋值。
本地窗口显示局部变量的当前值。例如下图自己定义的变量:
单步执行调试程序:
1.调试----逐语句(按快捷键是F8)
2.在逐语句执行过程中,遇到调用的过程,不想进入其中停留,按shift+F8,可以“逐过程”执行而不进入!
3.跳出过程
调试----跳出(快捷键是ctrl+shift+F8),可以运行当前过程剩余语句代码,转到调用该过程语句的下一个语句去!
4.执行特定的语句块
这是很重要的一个方法,常用到:在中断模式下,把光标移到要执行的语句行处,执行 调试---设置下一条语句,
用它可以设置下次要执行的语句。再把光标移到要停止的语句处,执行 调试----运行到光标处(快捷键是ctrl+F8),则可以运行到该语句处!
这种方法的优点是可以绕过不想执行的语句块(例如你明确知道某个有问题的语句)。
但是可以执行 工具----选项----编辑器----自动语法检测,则在代码窗口输入代码产生语法错误时,会立即显示出错误信息!
调用堆栈:应用程序处于中断模式时,显示已被调用过的过程!
1.怎么显示调用堆栈的对话框:视图---调用堆栈(快捷键是ctrl+L)
2.跟踪嵌套过程
在VBA过程中,常常有调用另外某个或某几个过程,模块,窗体。
可以用“调用堆栈”从下往上显示已活化过程的项目名称,模块或窗体名称和过程名称。
操作方法是:ctrl+L ----用鼠标选中某个过程-----显示。
这时,代码窗口显示出该过程,光标处于即将调用下一个过程的调用语句处。
此时,按(shift+F9)用“快速监视”或(ctrl+G)立即窗口,可以显示有关变量!
3.用“调用堆栈”来检查过程调用自身的能力
出处:http://club.excelhome.net/thread-225648-1-1.html
如何调试Excel VBA代码
Excel VBA出错时给出的错误信息极少,需要充分利用各种工具来进行调试。
1.编译错误
常见的编译错误有:
错误的源代码格式,比如if后面缺少then:在编辑器中该行会变成红色。
错误的语法结构,比如if和end if没有对应上:代码运行前会给出编译错误提示。
类型不匹配:函数输入的参数与定义时的参数类型不同时会出现该类错误。
变量未定义:指使用了没有申明的变量类型(当Option Explicit时)
建议:
格式规范化,严格缩进。VBA插件Smart Indent是一个很好的辅助工具。这样做让源代码更具备可读性,从而更快检测源代码格式和语法结果错误,。
勾选上「工具——选项——编译器——要求变量声明」,或者在每个代码模块最前面手工加上 Option Explicit 。在代码编辑中,很多错误只是因为手误,这个选项会让编译器强制检查变量申明,从而在编译时便发现错误。
2.运行中出错或者运行结果错误
当程序编译没有问题,但运行中出错或者运行的结果与想象中不符,就需要用到VBA的调试功能。下面是VBA提供的几个重要的调试功能,这些功能能让程序停在某些特定的位置上等待检查:
F9:设置程序断点,在代码左侧栏点击有相同效果。重新按F9即取消断点。
F8:单步跟踪,当调用子函数会跟踪到子函数内部
Shift+F8:单步跟踪,但不会进入子函数内部
Ctrl+Shift+F8:跳出正在跟踪的函数,直接返回上一层函数。
F5:运行程序,直到出现错误、程序结束或程序断点为止
Ctrl+F8:运行程序,直到出现错误、程序结束、程序断点或当前光标所在行为止
debug.print var:在立即窗口中显示var的值
debug.assert var:当var==false时程序自动停止
以上命令也可在菜单和命令栏中获取。
在调试过程中,可通过下面几种方法查看各个变量的值,当变量和预期不一样时,也就找到了程序出错的原因,便能对照修改:
立即窗口(快捷键Ctrl+G):在该窗口里会显示debut.print的结果值,以及随时计算和运行代码。在代码前面添加「?」,可以在立即窗口中显示运行结果。
监视窗口:可以将变量以及表达式添加到监视窗口,可以实时查看变量和表达式的值。支持将代码窗口里的变量和表达式拖入到见识窗口
本地窗口:本地窗口里可以查看目前的local变量和global变量的变量值。
编辑窗口:将鼠标停在编辑窗口的变量上可显示该变量的值。
有一些方法可减少程序错误以及降低调试的难度:
添加重要的代码注释。
源代码格式规范化,增加代码可读性。VBA插件Smart Indent是一个很好的辅助工具。
尽可能声明变量类型,少用Variant变量。
注意函数的参数传递方式,默认为传引用,子函数会修改变量的值。
尽量将功能函数化,不同的功能分开写。
避免在VBA中引用绝对地址,如Range("A1")。可先在Excel中定义名称"abc=AA1",然后引用Range("abc")。
避免使用ActiveSheet, ActiveWorkbook等可变变量,用Sheet1,ThisWorkbook这种绝对变量。绝对变量不受外界操作影响。
出处:http://www.elsyy.com/s/article/view-2473-11703
========================================================================
VBA测试与错误处理
1.测试
测试代码,检查它是否正确。为确保代码能够正常作用,必须尽力模拟代码运行的不同环境,并检查其正确性。
程序中的错误一般分为如下几类:
(1)语法错误(如输入时关键字或标点符号拼写错误) 。
(2)编译错误(如使用对象的方法时,该对象并不支持这个方法) 。
(3)运行期间错误(如除0、打开或关闭并不存在的文档、关闭未打开的文档等) 。
(4)逻辑错误(逻辑锗误是最难查找的错误类型,它们是由程序中使用的逻辑引起的。
2.调试
要确保代码中的变量和表达式按照预想的方式运行,可以使用VBA中的各种调试工具跟踪它们。VBE有以下调试工具。
(1) 中断模式
①进入中断模式方法:在过程的开始位置按下F8键(“逐语句”执行)。使用“切断断点“(F9键或者单击当前空白指示器栏)。使用Stop语句。当过程运行时,按下Ctrl+Break键。使用运行期间错误MsgBox对话框。
②退出中断模式方法:从运行菜单中选择“运行子过程/用户窗体)”;或者按下F5键,或者单击“调试”工具栏上的“运行子过程/用户窗体”按钮。从“运行”菜单中选择“重新设置”选项,或者单击“调试”工具栏上的“重新设置”按钮。
(2) 单步执行过程
① 单步执行过程:从“调试”菜申中选择“逐过程”选项,或者按下Shift+F8键。
②跳出过程(跳过程剩余部份):从“调试”菜单中选择“跳出”选项,或者按下Ctrl十shift十F8键。
③运行到光标处(跳过一组语句):从“调试”菜单中选择“运行到光标处”或按下Ctrl+F8键。
④设置下一条语句为光标所指语句(要警惕会忽略不执行中间的语句):从“调试”菜单中选择“设置下一条语句”或者按下Ctrl十F9键。
⑤“显示下一条语句”选项指明下一个将要执行的语句是什么。当需要在程序窗口中监视很多过程、却丢失了跟踪时,就可使用这个调试功能。
(3) 监视过程
①“本地”窗口(显示当前过程中使用所有变量及其值,还会显示当前加载窗体和控件的属性。):从“视图”菜单中选“本地窗口”选项。
②“调用堆栈”对话框(查看所有活动的过程调用):从“视图”菜单中选“调用堆栈”或按下Ctrl+L键。
③“立即窗口”(可查询并设置变量值,创建或撤销对象,执行单行命令)。从“视图”菜单中选择“立即窗口”或按下Ctrl+G键。
④“监视窗口”(是一个调试工具,在中断模式下可使用它更改变量和表达式的值,以查看不同的值如何影响代码)。从“视图”菜单中选择。
3.预防错误
(1)注释 。
(2)缩进代码:Tab键或启用Too1s(工具)菜单选Options(选项)Editor(编辑器)选项卡中的Auto Indent(自动缩进)。
(3)使代码模块化。
(4)显式声明变量:使用Option Explicit语句,或启用工具菜单选“选项”编辑器选项卡中的“需要变量声明”复选框。
(5)避免使用变体Variant(当某变量可能包含NULL值时就只能惟一指定Variant类型了)。
(6)打开语法检查:启用工具菜单选“选项”编辑器选项卡中“自动语法检测”。
(7)谨防Dim陷阱:Dim str1, str2 As String '并未声明str1变量。
4.错误处理
调试只能够发现可以预测的错误,要处理不可预测的和不可避免的错误时,就必须使用错误处理。通过启用错误处理,捕获并提示错误处理,可以使程序更健壮。就可以使应用程序更稳定、更健壮。如果应用程序中包含了好几个过程,那么可以考虑采用集中式错误处理程序。
(1) 捕获错误:On Error Goto line (On Error Goto 0会在当前过程禁用错误处理程序) 在这个语法中,line指定了发生运行期间错误时控制将跳转到的代码行。
(2) 编写错误处理程序:给用户显示错误信息;提示补救方法;允许继续或取消操作。使用Err对象:
属性 |
说明 |
Number |
存储最后一个错误的数字ID。这是默认属性 |
Description |
存储说明错误的有关信息 |
Source |
包含发生了错误的对象名或应用程序名 |
HelpFile |
包含帮助文件的名字 |
HelpContextID |
包含错误号对应的帮助上下文ID |
LastDLLError |
包含最后一次调用DLL的系统错误代码 |
Raise方法可以让用户自定义错误处理信息,还可把错误处理信息传回调用过程: Err.Raise number[, source, description, helpFile, helpContext]
Clear方法清除Err对象的所有属性值:Err.Clear (当调用On Error, Exit Sub, Exit Function, Exit Property, Resume 等语句时,会自动调用Err.Clear方法。)
(3) 退出错误处理程序:
①0n error Resume:当错误处理程序已经修复错误后,可以用它来返回控制给引起错误的语句。但注意如果错误还在的话,会引起无限循环。
②0n error Resume Next:忽略错误语句,继续下一句。
③Resume [LineLabel]: 把控制传递给行标号(必须和Resume语句处在同一个过程中)所在点的语句。
④也可使用Exit Sub或者Exit Function甚至End语句退出已产生错误的过程,但这些语句应放在错误处理程序之前,这样当未产生错误时就不会执行错误处理程序。
出处:http://blog.163.com/xing_aixin/blog/static/372355052008377252250/
VBA程序的调试
VBA程序的调试:设置断点、单步跟踪、设置监视窗
Acces的VBE编程环境提供了完整的一套调试工具和调试方法。熟练掌握好这些调试工具和调试方法的使用,可以快速、准确地找到问题所在,不断修改,加以完善。
一、“断点”概念
所谓“断点”就是在过程的某个特定语句上设置一个位置点以中断程序的执行。“断点”的设置和使用贯穿在程序调试运行的整个过程。
“断点”设置和取消有4种方法:
(1)选择语句行,单击“调试”工具栏中的“切换断点”可以设置和取消“断点”。
(2)选择语句行,单击“调试”菜单中的“切换断点”项可以设置和取消“断点”。
(3)选择语句行,按下键盘“F9”键可以设置和取消“断点”。
(4)选择语句行,鼠标光标移至行首点击可以设置和取消“断点”。
在VBE环境里,设置好的“断点”行是以“酱色”亮红显示,如图8.25所示。
二、调试工具的使用
VBE环境中,右键点击菜单空白位置,弹出快捷菜单,选中“调试”选项使其前边“?”出现,这时就会打开“调试”工具栏.如图8.24所示。
调试工具栏中主要按钮功能说明见表8.11所示。
调试工具一般是与“断点”配合使用进行各种调试操作。下面简要介绍“调试”工具栏上的一些主要调试工具的用法。
1.中断工具钮
用于暂时中断程序运行,进行分析。此时,在程序中序中断位置会产生一个“黄色”亮杠,如图8.25所示。
2.本地窗口工具钮
用于打开“本地窗口”窗口,如图8.26所示。其内部自动显示出所有在当前过程中的变量声明及变量值,从中可以观察各种数据信息。
本地窗口打开后,列表中的第一项内容是一个特殊的模块变量。对于类模块,定义为Me。Me是对当前模块定义的当前类实例的引用。由于它是对象引用,因而可以展开显示当前实例的全部属性和数据成员。
实际上,Me类模块变狱还广泛用于VBA程序设计中,它不需要专门定义,可以直接使用。一般编写类模块时,对当前模块的实例引用就可以使用Me关键字。
3.立即窗口工具钮
用于打开“立即窗口”窗口,如图8.27所示。在中断模式下,立即窗口中可以安排一些调试语句,而这些语句是根据显示在立即窗口区域的内容或范围来执行的。如果输入Print variablename,则输出的就是局域变量的值。
4.监视窗口工具钮
用于打开“监视窗口,窗口,如图8.28所示。在中断模式下,右键点击监视窗口区域会弹出如图所示的快捷菜单,选择“编辑监视…”或“添加监视…”项,则打开“编辑(或添加)窗口”,在表达式位置进行监视表达式的修改或添加,见图8.29所示,选择“删除监视…”项则会删除存在的监视表达式。
通过在监视窗口增添监视表达式的方法,程序可以动态了解一些变量或表达式的值的变化
情况,进而对代码的正确与否有清楚的判断。
5.快速监视工具钮
在中断模式下,先在程序代码区选定某个变量或表达式,然后点击“快速监视”工具钮,则打开“快速监视”窗口,如图8.30所示,从中可以快速观察到该变量或表达式的当前值,达到了快速监视的效果。如果需要,还可以点击“添加”按钮,将该变量或表达式添加到随后打开的“监视窗口”窗口中,以做进一步分析。
vba传递参数类型错误
先来看看微软官方的说明:
目录内容
概要
类型不匹配: 数组或预期的用户定义类型
解决方案
更多信息
重现问题的步骤
- 在 Microsoft Excel 中创建一个新工作簿。
- 从插入菜单中,选择宏,然后选择模块。
在 Microsoft Excel 97 中,请按 ALT + F11 来激活 Visual Basic 编辑器中。然后,在插入菜单中单击模块。 - 在模块中键入以下过程:
Sub First() Dim A(1 to 20) As Double Second A() End Sub Sub Second(B() As Single) End Sub
- 从工具菜单中,选择宏。选择首先,名为的宏,然后选择运行。
在 Microsoft Excel 97 中,请单击工具菜单上的宏。首先,单击然后单击运行。
此时,您应该收到上述错误消息之一。
Sub Correct() Dim A(1 to 20) As Double Example A() End Sub Sub Example(B() As Double) End Sub
出处:http://support.microsoft.com/kb/121114/
=============================================================
下面再看看网上其他人的解释,及解决方法
'简单的测试程序
Private Sub Form_Load()
Dim ii As Integer
Dim ba(), bb() As Byte
ReDim ba(9)
ReDim bb(9)
For ii = 0 To 9
ba(ii) = 3 * ii + 2
Next
Call ttsub(ba(), bb())
End Sub
Private Sub ttsub(a() As Byte, b() As Byte)
Dim aa As Byte
For aa = 0 To 9
b(aa) = a(aa) + 1
Next
End Sub
调试时提示下述错误:
compile error:
type mismatch: array or user-defined type expected
错误指向Call ttsub(ba(), bb())一句中的ba()
请问这段程序哪儿错了?该怎么改?
解决方法:
Dim ba(), bb() As Byte
这个是错误的.要写就写全,因为vb默认是变体Variant类型的.
所以千万不能马虎,得写Dim ba() As Byte, bb() As Byte
- 提问者评价:原来是这样,感谢!
VBA的过程及参数详解
这里主要介绍子程序的使用方法,同样这些方法也可以应用到Function上。
语法
[Private | Public | Friend] [Static] Sub name [(arglist)]
[statements]
[Exit Sub]
[statements]
End Sub
* 用[]符号括起来的选项是可选项
[Private | Public | Friend]
这三个关键字与作用范围有关。
Private表示私有,即这个过程只能从本模块里面调用。使用这个关键字,从菜单“工具”->”宏”->”宏…”中将看不到该过程。
Public表示公用,这样从其它的模块也可以访问这个过程。如果没有使用 Public、Private 或 Friend 显式指定,Sub 过程按缺省情况就是公用的。公用的过程可以从菜单”工具”->”宏”->”宏…”中看到。
Friend用在类模块里面,较少使用,在此就不介绍了。
Static是静态的意思(你可能还记得用Static声明静态变量),用它声明过程的话,表示这个过程中声明的局部变量在下次调用这个过程时仍然保持它原来的值。
下面是Static声明过程的用法。
Static Sub m1() Dim i As Integer Dim j As Integer i = i + 1 j = j + 1 Debug.Print "i=" & i & " j=" & j End Sub Private Sub m2() Dim i As Integer Dim j As Integer i = i + 1 j = j + 1 Debug.Print "i=" & i & " j=" & j End Sub Sub try1() Dim i As Integer Debug.Print "静态过程:" For i = 1 To 10 Call m1 Next i Debug.Print "私有过程:" For i = 1 To 10 Call m2 Next i End Sub
运行try1过程,然后可以在立即窗口里看到结果。
=
下面是一些使用过程时需要注意到地方。
- Sub过程可以是递归的;也就是说,该过程可以调用自己来完成某个特定的任务。不过,递归可能会导致堆栈上溢。通常 Static 关键字和递归的 Sub 过程不在一起使用。
- 所有的可执行代码都必须属于某个过程。不能在别的 Sub、Function 或 Property 过程中定义 Sub 过程。
- Exit Sub 语句使执行立即从一个 Sub 过程中退出。程序接着从调用该 Sub 过程的语句下一条语句执行。在 Sub 过程的任何位置都可以有 Exit Sub 语句。
- 在Sub过程中使用的变量分为两类:一类是在过程内显式定义的,另一类则不是。在过程内显式定义的变量(使用 Dim或等效方法)都是局部变量。对于使用了但又没有在过程中显式定义的变量,除非其在该过程外更高级别的位置有显示地定义,否则也是局部的。
- 小心过程可以使用没有在过程内显式定义的变量,但只要有任何在模块级别定义的名称与之同名,就会产生名称冲突。如果过程中使用的未定义的变量与别的过程,常数,或变量的名称相同,则认为过程使用的是模块级的名称。显式定义变量就可以避免这类冲突。可以使用 Option Explicit 语句来强制显式定义变量。
- 注意:不能使用 GoSub、GoTo 或 Return 来进入或退出 Sub 过程。
参数表arglist
语法:
[Optional] [ByVal | ByRef] [ParamArray] varname[( )] [As type] [= defaultvalue]
Optional表示这个参数是可选的,也就是说在调用过程时可以不传递值也可以传递值给这个参数.。如果有传递defaultvalue给这个参数时(如optional iInput2 As Integer=13), 当调用过程没有传递值给这个参数时,在过程中会默认使用这个defaultvalue。
Optional必须对最后面的那些参数使用,也就是说某个参数使用了Optional,该参数后面的参数也必须使用Optional。
Sub mmm(iInput1 As Integer, Optional iInput2 As Integer = 13, Optional iInput3 As Integer) 'iInput2用了Optional后,iInput3也必须用Optional MsgBox "iInput1=" & iInput1 & vbCrLf & "iInput2=" & iInput2 & vbCrLf & "iInput3=" & iInput3 End Sub Sub subTry() ' 可以给3个参数都赋值 Call mmm(23, 34, 2) 'iInput=23, iInput2=34, iInput3=2 '也可以给第3个赋值,而不给第2个参数赋值,这样iInput2会等于默认值13 Call mmm(23, , 2) 'iInput=23, iInput2=13, iInput3=2 '可以给第2个参数都赋值而不给第3个参数赋值 Call mmm(23, 34) 'iInput=23, iInput2=34, iInput3=0 ' 第2个,第3个参数都不赋值 Call mmm(23) 'iInput=23, iInput2=12, iInput3=0 End Sub
ParamArray的用法
ParamArray只能用于 arglist 的最后一个参数,指明最后这个参数是一个包含Variant类型元素的 Optional 数组,但你传递值给过程时还是使用逗号分开多个参数,过程里面会把找几个参数合并成一个数组。使用 ParamArray 的好处是你可以提供不定数目不定类型的参数给过程。ParamArray不能与 ByVal,ByRef,或 Optional 一起使用。
Sub m1(iInput1 As Integer, ParamArray argArr()) Dim strList As String Dim i As Integer strList = "iInput1=" & iInput1 & vbCrLf For i = 0 To UBound(argArr) On Error Resume Next strList = strList & "argArr(" & i & ")=" & argArr(i) & vbCrLf ' 如果参数为空,将生成错误 If Err.Description <> "" Then strList = strList & "argArr(" & i & ")=缺失参数" & vbCrLf Err.Clear ' 清除错误 End If Next i MsgBox strList End Sub Sub try1() Call m1(23, 24, 25) ' iInput=23, argArr(0)=24, argArr(1)=25 Call m1(23, 24, , 25) ' iInput=23, argArr(0)=24, argArr(1)=缺失参数, argArr(2)=25 Call m1(23, 24, 64.4, 25, "data") ' iInput=23, argArr(0)=24, argArr(1)=64.4, argArr(2)=25, argArr(3)=data End Sub
ByVal和ByRef
VBA中默认使用ByRef。ByVal的意思是按值传递参数,因为是按值传递,这个参数在过程里面的值有变化的话它影响的范围只是在这个过程里面。出了过程就没有用了。而ByRef是按地址或者说按引用传递,传递给过程的实际是这个数值的地址,而不是值本身,在过程中对改变这个参数也就是改变这个地址的值,这样在过程外面也可以看到这个值被改变了。运行下面的例子可以看到其中的区别。
Sub mmm(ByVal iI1 As Integer, iI2 As Integer) iI1 = iI1 + 10 iI2 = iI2 + 10 MsgBox "Inside: iI1=" & iI1 & " iI2=" & iI2 End Sub Sub mySub() Dim iI1 As Integer Dim iI2 As Integer iI1 = 10 iI2 = 12 MsgBox "Before: iI1=" & iI1 & " iI2=" & iI2 Call mmm(iI1, iI2) MsgBox "After: iI1=" & iI1 & " iI2=" & iI2 ' 按顺序分别显示 ' 显示 Before: iI1=10 iI2=12 ' 显示 Before: iI1=20 iI2=22 ' 显示 Before: iI1=10 iI2=22 ' 过程mmm中修改了iI1和iI2,但是iI1是按值传递,在mmm过程之外的iI1并受影响 ' 而iI2是按引用传递,mmm过程之外的iI2也被改变了 End Sub
另外,如果参数是数组的话,只能使用按引用传递,因为传递的实际上是数组第一个元素的地址。例如下面代码的用法。
Sub GetArray(arrTemp() As Integer) Dim i As Integer For i = 0 To UBound(arrTemp) Debug.Print "Item " & i & ": " & arrTemp(i) Next i End Sub Sub PassArray() Dim arrInt(3) As Integer arrInt(0) = 1 arrInt(1) = 2 arrInt(2) = 3 arrInt(3) = 4 Call GetArray(arrInt) ' 在立即窗口打印出 ' Item 0: 1 ' Item 1: 2 ' Item 2: 3 ' Item 3: 4 End Sub
但对于对象来说,使用ByVal实际上传递的仍然是对对象的引用,这样在过程中对象的修改将会影响过程外部对象的值或属性。如果使用ByVal,而在过程中创建一个新的对象实例,将该对象赋值给传递的对象,则不影响调用对象的外部的属性或值。而如果使用ByRef,在过程中创建一个新的对象实例,将该对象赋给传递的对象,却会影响过程外该对象的属性或值。
Sub TestByValByRef() Dim objDic As Object Set objDic = CreateObject("Scripting.Dictionary") objDic(1) = 100 Debug.Print "byValueTest1" Debug.Print "原始值: objDic(1)=" & objDic(1) Call byValTest1(objDic) Debug.Print "外部值: objDic(1)=" & objDic(1) & vbCrLf objDic(1) = 100 Debug.Print "byValueTest2" Debug.Print "原始值: objDic(1)=" & objDic(1) Call byValTest2(objDic) Debug.Print "外部值: objDic(1)=" & objDic(1) & vbCrLf objDic(1) = 100 Debug.Print "byRefTest" Debug.Print "原始值: objDic(1)=" & objDic(1) Call byRefTest(objDic) Debug.Print "外部值: objDic(1)=" & objDic(1) End Sub Private Sub byValTest1(ByVal c As Object) Dim a As Object Set a = CreateObject("Scripting.Dictionary") a(1) = 200 Set c = a End Sub Private Sub byValTest2(ByVal c As Object) c(1) = 200 End Sub Private Sub byRefTest(ByRef c As Object) Dim a As Object Set a = CreateObject("Scripting.Dictionary") a(1) = 200 Set c = a End Sub
运行过程TestByValByRef,将在立即窗口打印出下面的结果。
byValueTest1
原始值: objDic(1)=100
外部值: objDic(1)=100byValueTest2
原始值: objDic(1)=100
外部值: objDic(1)=200byRefTest
原始值: objDic(1)=100
外部值: objDic(1)=200
出处:http://www.360doc.com/content/10/0112/16/406571_13355136.shtml
vba打开excel文件遍历sheet的名字和指定单元格的值
今天项目上有个应用,获取指定Excel文件下的所有sheet的名称以及当前sheet中指定单元格的值,并把他们写到固定的sheet中去,看了下,文件比较多,而且每个文件sheet的个数比较多,也不一样,所以打算写个程序来帮我们实现任务,代码很简单,也写的比较死板。欢迎大家给出意见及你的代码:
Sub aaaa() Dim sh1, sh2 As Worksheet Dim shName, cellValue As String 'On Error Resume Next Set sh1 = Workbooks(1).Sheets(1) 'Workbooks.Open Filename:="D:\_jack\Finance Report\Report Layout\TA Opex Report 2014.xlsx" Workbooks.Open Filename:="D:\_jack\Finance Report\Report Layout\Rolling PL Template v1 (2).xlsx" For i = 1 To Workbooks(2).Worksheets.Count Workbooks(2).Activate Set sh2 = ActiveWorkbook.Worksheets(i) sh2.Activate n = sh2.Name 'v = sh2.Cells(5, 7).Value 'G5单元格 v = sh2.Cells(4, 3).Value 'C4单元格 Workbooks(1).Activate sh1.Activate sh1.Cells(i, 1) = n sh1.Cells(i, 2) = v Next Workbooks(2).Close End Sub