Скрытые возможности VBA

Какие функции языка VBA плохо документированы или просто не часто используются?

16 ответов

Этот трюк работает только в Access VBA, Excel и другие не позволят. Но вы можете сделать стандартный модуль скрытым от обозревателя объектов, поставив перед именем модуля знак подчеркивания. Модуль будет виден только в том случае, если вы измените браузер объектов для отображения скрытых объектов.

Этот прием работает с Enums во всех версиях VBA на основе vb6. Вы можете создать скрытого члена 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 вы можете ссылаться на ячейки, заключая их в квадратные скобки, квадратные скобки также функционируют как команда оценки, позволяющая оценить синтаксис формулы:

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

Также вы можете передавать необработанные данные без использования MemCopy (RtlMoveMemory), комбинируя LSet с пользовательскими типами того же размера:

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

"Середина" тоже оператор. Используя его, вы перезаписываете определенные части строк без общеизвестно медленной конкатенации строк 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 те же возможности StringBuilder, что и.Net.

' 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, даже не подозревают, что это даже часть пакета.

Я разместил это на нескольких вопросах здесь, но Object Browser - мое секретное оружие. Если мне нужно быстро написать код ниндзя, но я не знаком с DLL, Object Browser спасает мне жизнь. Это значительно облегчает изучение структур классов, чем MSDN.

Окно Locals отлично подходит для отладки. Поместите паузу в ваш код, и он покажет вам все переменные, их имена, а также их текущие значения и типы в текущем пространстве имен.

А кто может забыть нашего хорошего друга Immediate Window? Он не только хорош для стандартного вывода 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

Возможно, наименее документированные функции в VBA - это те, которые вы можете открыть, выбрав "Показать скрытых участников" в Обозревателе объектов VBA. Скрытые члены - это те функции, которые есть в VBA, но не поддерживаются. Вы можете использовать их, но Microsoft может устранить их в любое время. Ни у одного из них нет документации, но вы можете найти ее в Интернете. Возможно, наиболее обсуждаемые из этих скрытых функций предоставляют доступ к указателям в VBA. Для достойной рецензии, проверьте; Не такой легкий - Shlwapi.dll

Документировано, но, возможно, более неясно (в любом случае в Excel) использует ExecuteExcel4Macro для доступа к скрытому глобальному пространству имен, которое принадлежит всему экземпляру приложения Excel, а не конкретной книге.

Скрытые возможности

  1. Хотя это "Базовый", вы можете использовать ООП - классы и объекты
  2. Вы можете делать вызовы API

Словари. VBA практически ничего не стоит без них!

Обратитесь к Microsoft Scripting Runtime, используйте Scripting.Dictionary для любой достаточно сложной задачи, и жить долго и счастливо.

Среда выполнения сценариев также предоставляет FileSystemObject, что также настоятельно рекомендуется.

Начни здесь, потом копайся немного...

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

Вы можете реализовать интерфейсы с Implements ключевое слово.

Typing VBA. вызовет список всех встроенных функций и констант intellisense.

Немного поработав, вы можете перебирать пользовательские коллекции, например так:

' 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
  • Сохранить 4 целых нажатия клавиш, набрав debug.? xxx вместо debug.print xxx,
  • Сбой, добавив: enum foo: me=0: end enum наверх модуля, содержащего любой другой код.

Поддержка локализованных версий, которые (по крайней мере, в прошлом веке) поддерживали выражения с использованием локализованных значений. Как "Правда" для True и Fałszywy (не слишком уверен, но, по крайней мере, у него была забавная буква "L") для False на польском... На самом деле английская версия могла бы читать макросы на любом языке и конвертировать на лету. Другие локализованные версии не будут обрабатывать это, хотя.

ПОТЕРПЕТЬ ПОРАЖЕНИЕ.

IsDate("13.50") возвращается True но IsDate("12.25.2010") возвращается False

Это потому что IsDate может быть более точно назван IsDateTime, И потому, что период (.) рассматривается как разделитель времени, а не как разделитель даты. Смотрите здесь для полного объяснения.

Объектная модель VBE (расширяемость Visual Basic) является менее известной и / или недостаточно используемой функцией. Это позволяет вам писать код VBA для управления кодом VBA, модулями и проектами. Однажды я написал проект Excel, который собирал другие проекты Excel из группы файлов модулей.

Объектная модель также работает с VBScript и HTA. Однажды я написал HTA, чтобы помочь мне отслеживать большое количество проектов Word, Excel и Access. Во многих проектах использовались бы модули общего кода, и для модулей было легко "расти" в одной системе, а затем их нужно было перенести в другие системы. Мой HTA позволил бы мне экспортировать все модули в проекте, сравнить их с версиями в общей папке и объединить обновленные подпрограммы (используя BeyondCompare), а затем повторно импортировать обновленные модули.

Объектная модель VBE работает немного по-разному между Word, Excel и Access, и, к сожалению, вообще не работает с Outlook, но все же предоставляет отличные возможности для управления кодом.

VBA поддерживает побитовые операторы для сравнения двоичных цифр (битов) двух значений. Например, выражение 4 И 7 оценивает значения битов 4 (0100) и 7 (0111) и возвращает 4 (бит, который включен в обоих числах.) Аналогично выражение 4 И 8 оценивает значения битов в 4 (0100) и 8 (1000) и возвращает 12 (1100), то есть биты, где любой из них равен true.

К сожалению, побитовые операторы имеют одинаковые имена у операторов логического сравнения: And, Eqv, Imp, Not, Or и Xor. Это может привести к неясностям и даже противоречивым результатам.

Например, откройте окно "Немедленно" (Ctrl+G) и введите:? (2 и 4) Возвращает ноль, поскольку между 2 (0010) и 4 (0100) нет общих битов.

Deftype Заявления

Эта функция существует, предположительно, для обратной совместимости. Или написать безнадежно запутанный код спагетти. Твой выбор.

Другие вопросы по тегам