【发布时间】:2010-11-07 10:30:09
【问题描述】:
VBA 语言的哪些功能文档记录不充分,或者根本不经常使用?
【问题讨论】:
-
@bbqchickenrobot 我没有太多选择,真的。无论如何,在小剂量情况下,它并没有那么糟糕。
标签: vba hidden-features
VBA 语言的哪些功能文档记录不充分,或者根本不经常使用?
【问题讨论】:
标签: vba hidden-features
此技巧仅适用于 Access VBA,Excel 和其他不允许。但是您可以通过在模块名称前加上下划线来使标准模块对对象浏览器隐藏。只有当您将对象浏览器更改为显示隐藏对象时,该模块才会可见。
此技巧适用于所有基于 vb6 的 VBA 版本中的枚举。您可以通过将其名称括在括号中来创建 Enum 的隐藏成员,然后在其前面加上下划线。示例:
Public Enum MyEnum
meDefault = 0
meThing1 = 1
meThing2 = 2
meThing3 = 3
[_Min] = meDefault
[_Max] = meThing3
End Enum
Public Function IsValidOption(ByVal myOption As MyEnum) As Boolean
If myOption >= MyEnum.[_Min] Then IsValidOption myOption <= MyEnum.[_Max]
End Function
在 Excel-VBA 中,您可以通过将单元格括在方括号中来引用单元格,方括号还可以用作 evaluate command,允许您评估公式语法:
Public Sub Example()
[A1] = "Foo"
MsgBox [VLOOKUP(A1,A1,1,0)]
End Sub
您还可以通过将 LSet 与相同大小的用户定义类型相结合,在不使用 MemCopy (RtlMoveMemory) 的情况下传递原始数据:
Public Sub Example()
Dim b() As Byte
b = LongToByteArray(8675309)
MsgBox b(1)
End Sub
Private Function LongToByteArray(ByVal value As Long) As Byte()
Dim tl As TypedLong
Dim bl As ByteLong
tl.value = value
LSet bl = tl
LongToByteArray = bl.value
End Function
八进制和十六进制文字实际上是无符号类型,它们都将输出 -32768:
Public Sub Example()
Debug.Print &H8000
Debug.Print &O100000
End Sub
如前所述,在括号内传递变量会导致它被 ByVal 传递:
Sub PredictTheOutput()
Dim i&, j&, k&
i = 10: j = i: k = i
MySub (i)
MySub j
MySub k + 20
MsgBox Join(Array(i, j, k), vbNewLine), vbQuestion, "Did You Get It Right?"
End Sub
Public Sub MySub(ByRef foo As Long)
foo = 5
End Sub
您可以将字符串直接分配到字节数组中,反之亦然:
Public Sub Example()
Dim myString As String
Dim myBytArr() As Byte
myBytArr = "I am a string."
myString = myBytArr
MsgBox myString
End Sub
“Mid”也是一个运算符。使用它,您可以覆盖字符串的特定部分,而无需 VBA 众所周知的慢字符串连接:
Public Sub Example1()
''// This takes about 47% of time Example2 does:
Dim myString As String
myString = "I liek pie."
Mid(myString, 5, 2) = "ke"
Mid(myString, 11, 1) = "!"
MsgBox myString
End Sub
Public Sub Example2()
Dim myString As String
myString = "I liek pie."
myString = "I li" & "ke" & " pie" & "!"
MsgBox myString
End Sub
【讨论】:
Mid() 语句有一个重要但几乎总是遗漏的特性。这就是 Mid() 出现在赋值的左侧,而不是 Mid() 函数出现在右侧或表达式中的位置。
规则是如果目标字符串不是字符串文字,并且这是对目标字符串的唯一引用,并且插入的段的长度与被替换的段的长度匹配,那么字符串将被视为可变的操作。
这是什么意思?这意味着,如果您将大型报告或大量字符串列表构建为单个字符串值,那么利用它将使您的字符串处理速度更快。
这是一个从中受益的简单类。它为您的 VBA 提供了与 .Net 相同的 StringBuilder 功能。
' Class: StringBuilder
Option Explicit
Private Const initialLength As Long = 32
Private totalLength As Long ' Length of the buffer
Private curLength As Long ' Length of the string value within the buffer
Private buffer As String ' The buffer
Private Sub Class_Initialize()
' We set the buffer up to it's initial size and the string value ""
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
Public Sub Append(Text As String)
Dim incLen As Long ' The length that the value will be increased by
Dim newLen As Long ' The length of the value after being appended
incLen = Len(Text)
newLen = curLength + incLen
' Will the new value fit in the remaining free space within the current buffer
If newLen <= totalLength Then
' Buffer has room so just insert the new value
Mid(buffer, curLength + 1, incLen) = Text
Else
' Buffer does not have enough room so
' first calculate the new buffer size by doubling until its big enough
' then build the new buffer
While totalLength < newLen
totalLength = totalLength + totalLength
Wend
buffer = Left(buffer, curLength) & Text & Space(totalLength - newLen)
End If
curLength = newLen
End Sub
Public Property Get Length() As Integer
Length = curLength
End Property
Public Property Get Text() As String
Text = Left(buffer, curLength)
End Property
Public Sub Clear()
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
下面是一个如何使用它的例子:
Dim i As Long
Dim sb As StringBuilder
Dim result As String
Set sb = New StringBuilder
For i = 1 to 100000
sb.Append CStr( i)
Next i
result = sb.Text
【讨论】:
VBA 本身似乎是一个隐藏功能。我认识的多年来一直使用 Office 产品的人都不知道它甚至是套件的一部分。
我已经在这里发布了多个问题,但对象浏览器是我的秘密武器。如果我需要 ninja 快速编写代码,但不熟悉 dll,对象浏览器可以挽救我的生命。它比 MSDN 更容易学习类结构。
Locals 窗口也非常适合调试。在您的代码中暂停一下,它将显示当前命名空间中的所有变量、它们的名称以及它们的当前值和类型。
谁能忘记我们的好朋友即时窗口?它不仅对 Debug.Print 标准输出非常有用,而且您也可以在其中输入命令。想知道什么是 VariableX?
?VariableX
需要知道那个单元格是什么颜色?
?Application.ActiveCell.Interior.Color
事实上,所有这些窗口都是使用 VBA 提高工作效率的好工具。
【讨论】:
这不是一个特性,而是我在 VBA(和 VB6)中多次看到错误的事情:在方法调用上添加括号,它将改变语义:
Sub Foo()
Dim str As String
str = "Hello"
Bar (str)
Debug.Print str 'prints "Hello" because str is evaluated and a copy is passed
Bar str 'or Call Bar(str)
Debug.Print str 'prints "Hello World"
End Sub
Sub Bar(ByRef param As String)
param = param + " World"
End Sub
【讨论】:
Call foo(bar) 总是可以替换为 foo bar 所以 Call 似乎是多余的。通过关于 SO 的 VBA 问题,许多人似乎不知道括号不需要调用 Sub,因此我们可以将其称为难以理解的功能
Bar(str) 而不是 Bar (str) 括号不要覆盖 ByRef 关键字。
Bar(str) 版本的?
隐藏的功能
【讨论】:
VBA 中记录最少的功能可能是那些您只能通过在 VBA 对象浏览器上选择“显示隐藏成员”来公开的功能。隐藏成员是那些在 VBA 中但不受支持的函数。您可以使用它们,但微软可能会随时删除它们。他们都没有提供任何文档,但你可以在网上找到一些。这些隐藏功能中谈论最多的可能是对 VBA 中指针的访问。要获得体面的文章,请查看; Not So Lightweight - Shlwapi.dll
已记录,但可能更模糊(无论如何在 excel 中)正在使用 ExecuteExcel4Macro 访问属于整个 Excel 应用程序实例而不是特定工作簿的隐藏全局命名空间。
【讨论】:
您可以使用Implements 关键字实现接口。
【讨论】:
字典。没有它们,VBA 几乎一文不值!
参考 Microsoft Scripting Runtime,将Scripting.Dictionary 用于任何足够复杂的任务,从此过上幸福的生活。
Scripting Runtime 还为您提供 FileSystemObject,强烈推荐。
从这里开始,然后挖掘一下......
http://msdn.microsoft.com/en-us/library/aa164509%28office.10%29.aspx
【讨论】:
输入 VBA. 将显示所有内置函数和常量的智能感知列表。
【讨论】:
通过一些工作,您可以像这样迭代自定义集合:
' Write some text in Word first.'
Sub test()
Dim c As New clsMyCollection
c.AddItems ActiveDocument.Characters(1), _
ActiveDocument.Characters(2), _
ActiveDocument.Characters(3), _
ActiveDocument.Characters(4)
Dim el As Range
For Each el In c
Debug.Print el.Text
Next
Set c = Nothing
End Sub
您的自定义集合代码(在名为 clsMyCollection 的类中):
Option Explicit
Dim m_myCollection As Collection
Public Property Get NewEnum() As IUnknown
' This property allows you to enumerate
' this collection with the For...Each syntax
' Put the following line in the exported module
' file (.cls)!'
'Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = m_myCollection.[_NewEnum]
End Property
Public Sub AddItems(ParamArray items() As Variant)
Dim i As Variant
On Error Resume Next
For Each i In items
m_myCollection.Add i
Next
On Error GoTo 0
End Sub
Private Sub Class_Initialize()
Set m_myCollection = New Collection
End Sub
【讨论】:
debug.? xxx 而不是 debug.print xxx 保存 4 个完整的击键。enum foo: me=0: end enum 以使其崩溃。【讨论】:
支持本地化版本,该版本(至少在上个世纪)支持使用本地化值的表达式。就像 Pravda for True 和 Fałszywy(不太确定,但至少它确实有有趣的 L)在波兰语中的 False ......实际上英语版本将能够阅读任何语言的宏,并即时转换。但是,其他本地化版本无法处理。
失败。
【讨论】:
VBE(Visual Basic 可扩展性)对象模型是一个鲜为人知和/或未充分利用的功能。它允许您编写 VBA 代码来操作 VBA 代码、模块和项目。我曾经写过一个 Excel 项目,它可以从一组模块文件中组装其他 Excel 项目。
对象模型也适用于 VBScript 和 HTA。我一次写了一个 HTA 来帮助我跟踪大量的 Word、Excel 和 Access 项目。许多项目会使用通用代码模块,模块很容易在一个系统中“增长”,然后需要迁移到其他系统。我的 HTA 允许我导出项目中的所有模块,将它们与公共文件夹中的版本进行比较并合并更新的例程(使用 BeyondCompare),然后重新导入更新的模块。
VBE 对象模型在 Word、Excel 和 Access 之间的工作方式略有不同,遗憾的是根本不能在 Outlook 中工作,但仍然提供了很好的代码管理功能。
【讨论】:
IsDate("13.50") 返回True 但IsDate("12.25.2010") 返回False
这是因为IsDate 可以更准确地命名为IsDateTime。并且因为句点 (.) 被视为时间分隔符而不是日期分隔符。见here for a full explanation。
【讨论】:
VBA 支持位运算符来比较两个值的二进制数字(位)。例如,表达式 4 And 7 计算 4 (0100) 和 7 (0111) 的位值并返回 4(这两个数字中的位)。类似地,表达式 4 Or 8 计算 4 (0100) 中的位值) 和 8 (1000) 并返回 12 (1100),即其中任一为真的位。
不幸的是,位运算符在逻辑比较运算符中具有相同的名称:And、Eqv、Imp、Not、Or 和 Xor。这可能会导致模棱两可,甚至产生矛盾的结果。
例如,打开即时窗口 (Ctrl+G) 并输入: ? (2和4) 这将返回零,因为 2 (0010) 和 4 (0100) 之间没有共同的位。
【讨论】:
此功能的存在大概是为了向后兼容。或者编写令人绝望的混淆意大利面条代码。你的选择。
【讨论】:
DEF FN。美好的时光……