29

VBA 语言的哪些特性要么记录不充分,要么根本不经常使用?

4

16 回答 16

32

This trick only works in Access VBA, Excel and others won't allow it. But you can make a Standard Module hidden from the object browser by prefixing the Module name with an underscore. The module will then only be visible if you change the object browser to show hidden objects.

This trick works with Enums in all vb6 based version of VBA. You can create a hidden member of an Enum by encasing it's name in brackets, then prefixing it with an underscore. Example:

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

In Excel-VBA you can reference cells by enclosing them in brackets, the brackets also function as an evaluate command allowing you to evaluate formula syntax:

Public Sub Example()
    [A1] = "Foo"
    MsgBox [VLOOKUP(A1,A1,1,0)]
End Sub

Also you can pass around raw data without using MemCopy (RtlMoveMemory) by combining LSet with User Defined Types of the same size:

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

Octal & Hex Literals are actually unsigned types, these will both output -32768:

Public Sub Example()
    Debug.Print &H8000
    Debug.Print &O100000
End Sub

As mentioned, passing a variable inside parenthesis causes it to be passed 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

You can assign a string directly into a byte array and vice-versa:

Public Sub Example()
    Dim myString As String
    Dim myBytArr() As Byte
    myBytArr = "I am a string."
    myString = myBytArr
    MsgBox myString
End Sub

"Mid" is also an operator. Using it you overwrite specific portions of strings without VBA's notoriously slow string concatenation:

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
于 2009-12-18T08:16:57.637 回答
18

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
于 2009-08-27T04:22:19.047 回答
15

VBA 本身似乎是一个隐藏功能。我认识的多年来一直使用 Office 产品的人都不知道它甚至是套件的一部分。

我在这里发布了多个问题,但对象浏览器是我的秘密武器。如果我需要 ninja 快速编写代码,但不熟悉 dll,对象浏览器可以挽救我的生命。它比 MSDN 更容易学习类结构。

Locals 窗口也非常适合调试。在您的代码中暂停一下,它将显示当前命名空间中的所有变量、它们的名称以及它们的当前值和类型。

谁能忘记我们的好朋友即时窗口?它不仅对 Debug.Print 标准输出非常有用,而且您也可以在其中输入命令。需要知道什么是 VariableX?

?VariableX

需要知道那个细胞是什么颜色?

?Application.ActiveCell.Interior.Color

事实上,所有这些窗口都是使用 VBA 提高生产力的好工具。

于 2009-07-01T21:45:09.497 回答
13

这不是一个功能,而是我在 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
于 2009-07-01T19:39:35.600 回答
7

VBA 中记录最少的功能可能是那些只能通过在 VBA 对象浏览器上选择“显示隐藏成员”来公开的功能。隐藏成员是那些在 VBA 中但不受支持的函数。您可以使用它们,但微软可能会随时删除它们。他们都没有提供任何文档,但你可以在网上找到一些。这些隐藏功能中谈论最多的可能是提供对 VBA 中指针的访问。要获得体面的文章,请查看;不是那么轻量级-Shlwapi.dll

记录在案,但可能更模糊(无论如何在 excel 中)是使用 ExecuteExcel4Macro 访问属于整个 Excel 应用程序实例而不是特定工作簿的隐藏全局命名空间。

于 2009-09-02T20:46:19.317 回答
7

隐藏的功能

  1. 虽然它是“基本”,但您可以使用 OOP - 类和对象
  2. 您可以进行 API 调用
于 2009-07-01T19:30:25.337 回答
6

字典。没有它们,VBA 几乎一文不值!

参考 Microsoft Scripting Runtime,Scripting.Dictionary用于任何足够复杂的任务,并从此过上幸福的生活。

Scripting Runtime 还为您提供了 FileSystemObject,这也是强烈推荐的。

从这里开始,然后挖掘一下......

http://msdn.microsoft.com/en-us/library/aa164509%28office.10%29.aspx

于 2010-03-18T04:00:09.907 回答
6

Implements您可以使用关键字实现接口。

于 2009-07-01T19:37:12.750 回答
5

键入VBA.将显示所有内置函数和常量的智能感知列表。

于 2010-08-23T14:33:06.053 回答
4
  • Save 4 whole keystrokes by typing debug.? xxx instead of debug.print xxx.
  • Crash it by adding: enum foo: me=0: end enum to the top of a module containing any other code.
于 2010-04-12T14:06:07.743 回答
4

通过一些工作,您可以像这样迭代自定义集合:

' 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
于 2009-07-01T20:09:56.070 回答
3

支持本地化版本,该版本(至少在上个世纪)支持使用本地化值的表达式。就像 Pravda for True 和 Fałszywy(不太确定,但至少它确实有有趣的 L)在波兰语中 False ......实际上英语版本将能够阅读任何语言的宏,并即时转换。但是,其他本地化版本无法处理。

失败。

于 2009-07-01T19:36:23.843 回答
2

VBE(Visual Basic 可扩展性)对象模型是一个鲜为人知和/或未充分利用的特性。它允许您编写 VBA 代码来操作 VBA 代码、模块和项目。我曾经写过一个 Excel 项目,它可以从一组模块文件中组装其他 Excel 项目。

对象模型也适用于 VBScript 和 HTA。我一次写了一个 HTA 来帮助我跟踪大量的 Word、Excel 和 Access 项目。许多项目会使用通用代码模块,模块很容易在一个系统中“增长”,然后需要迁移到其他系统。我的 HTA 将允许我导出项目中的所有模块,将它们与公共文件夹中的版本进行比较并合并更新的例程(使用 BeyondCompare),然后重新导入更新的模块。

VBE 对象模型在 Word、Excel 和 Access 之间的工作方式略有不同,遗憾的是根本不能与 Outlook 一起工作,但仍然提供了很好的代码管理功能。

于 2009-07-09T22:29:31.997 回答
2

IsDate("13.50")返回TrueIsDate("12.25.2010")返回False

这是因为IsDate可以更准确地命名IsDateTime。并且因为句点 ( .) 被视为时间分隔符而不是日期分隔符。有关完整说明,请参见此处。

于 2010-12-02T19:27:24.480 回答
1

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) 之间没有共同的位。

于 2010-03-29T21:10:46.930 回答
0

Deftype Statements

This feature exists presumably for backwards-compatibility. Or to write hopelessly obfuscated spaghetti code. Your pick.

于 2011-04-05T15:16:50.200 回答