VBasic, краткий справочник Печать
Добавил(а) microsin   

1. Перенос строки кода VBasic на следующую делается с помощью символа '_' в конце строки (можно применять не более 10 разделителей, и суммарная длина строки не должна превышать 1023 символа). В одной строке можно использовать два и более различных оператора, разделённых символом ':'. Комментарий указывается с помощью символа ' (можно использовать в любом месте строки) или оператора REM (только в начале строки).

2. Можно задавать переменные неявно, просто присваивая им значение. Переменные задаются явно с помощью оператора DIM:

DIM имя_переменной [AS тип_переменной]

Внимание! Через запятую можно указать несколько переменных, но для каждой ОБЯЗАТЕЛЬНО нужно отдельно указать тип (не так, как в C, с непривычки получается попадалово!). Как обычно, имя переменной должно начинаться с буквы и не может быть зарезервированным словом. Длина имени не может превышать 255 символов. Указывать тип данных при объявлении не обязательно, имеется возможность указать тип добавлением к имени переменной в конце специального символа (используется для совместимости со старыми версиями Basic, и Microsoft не рекомендует использовать эти символы):

Тип переменной   Символ   Пример
Integer % Dim IntegerSample%
Long & Dim LongSamle&
Single i Dim SingleSamlei
Double # Dim DoubleSample#
Currency @ Dim CurrencySample@
String $ Dim StringSample$

Если ни символ в конце, ни тип переменной явно не указан, то тип переменной становится Variant. VBasic всегда по умолчанию использует тип Variant. Тип хранимой величины по ходу выполнения может меняться. Использование такого типа чревато логическими ошибками в программе, которые трудно выявить, поскольку в ходе чтения кода тип данных не виден. Кроме того, для поддержки Variant расходуется больше памяти.

3. Видимость переменных - существует 3 вида:

- локальная (переменная доступна только в процедуре. Для этого она должна быть определена внутри неё).
- видимости контейнера (переменная доступна только в текущей форме, модуле или классе). Для этого переменная должна быть определена в секции General Declarations, Или, говоря по-русски, в начале листинга модуля.
- глобальная (видна во всех процедурах и модулях проекта. Для этого переменная опять-таки определяется в начале модуля, но уже с ключевым словом Public [раньше, до 5-й версии, использовалось ключевое слово Global]).

4. Время жизни переменных - локальные переменные при каждом выходе из процедуры удаляются из памяти, а при новом вызове инициализируются заново. Если это не нужно, то надо либо определить переменную в начале модуля (т. е. сделать её переменной контейнера или глобальной). Но при этом существует опасность (при совпадении имён) случайного изменения содержимого переменной в другом месте программы. Поэтому есть возможность сделать переменную в процедуре статической. Такая переменная сохраняет значение при выходе из процедуры, пока существует в памяти форма или модуль. Для этого вместо оператора Dim применяют оператор Static. Чтобы сделать все переменные процедуры статическими, процедура объявляется с оператором Static:

Static Sub AllVarsTheseSubAutoDeclaredAsStatic (...)
   ...
End Sub

5. Массивы в VBasic.

- Элементы массива всегда индексируются с нуля (стартовый элемент массива имеет индекс 0), но с помощью оператора Option Base 1, указанного в начале модуля, можно задать начало индексации с 1. Для установки других границ массива используют другой синтаксис:

[Static|Piblic|Dim] Имя_массива([Нижний_предел To] Верхний_предел)

Например:

Dim aBirthDate (1980 To 2050)

- Многомерный массив задаётся так:

Dim Название_массива (Измерение1, Измерение2, ..., ИзмерениеN)

Например:

Dim aStudent (5, 10, 30) ' 5-номер школы, 10-номер класса, 30-номер ученика
aStudent(3, 5, 17) = "Иванов"

Для отдельных размеров (измерений) тоже могут задаваться пределы индекса.

- Массивы бывают статические и динамические. Ключевое слово Static тут ни при чём! Статические массивы - это совсем не то, что имелось в виду, когда описывалось время жизни переменной (ключевое слово Static). Здесь подразумевается то, что размер (количество элементов) массива фиксированный (статический). Статический массив определяется так:

Dim SampleArray(150) As String   'статический массив из 150 строк

Теперь немного про динамические массивы. Динамические массивы создаются в 2 этапа. Сначала в начале контейнера - модуля, формы, класса - (по-другому эта область называется General Declarations) определяют массив без указания размера:

Dim aArray() As Variant

Затем внутри процедур с помощью оператора ReDim устанавливают фактический размер массива:

...
ReDim aArray (50,10)
...

Синтаксис оператора ReDim:

ReDim [Preserve] Имя_массива(Границы) [As Тип_данных]

В отличие от оператора Dim, оператор ReDim используется только в процедурах. При этом тип данных указывать не обязательно, особенно если он уже определён оператором Dim. Вы не можете переопределять тип данных массива, если он был заранее определён оператором Dim, за исключением того случая, когда был указан тип Variant (который, как мы знаем, является типом по умолчанию, т. е. если мы не указали тип, или указали тип Variant, эффект одинаковый).

Мы можем при необходимости менять размер массива, но! Нужно помнить, что записанные в массив данные мы при этом потеряем (они инициализируются значением по умолчанию. Каким? Молчит наука...). И тут опять но... Сохранить размерность массива позволяет ключевое слово Preserve:

Dim aArray() As Variant
...
Private Sub Subroutine1()
   ...
   ReDim Preserve aArray (50,15)
   ...
End Sub

Теперь при изменении размера массива его содержимое сохраняется. Но (которое по счёту?!) следует учитывать, что для многомерных массивов можно менять только последнее измерение.

Область видимости динамического массива бывает либо глобальной (если он был определён с помощью оператора Public), либо контейнерной (если с помощью Dim).
Практический пример использования одномерного массива с пользовательским типом данных:

'создаем пользовательский тип данных
Private Type tSklad
 Name As String
 Summa As Double
 Kol As Long
End Type
Dim Store() As tSklad
   ...
'инициализируем массив
Erase Store
ReDim Store(0)   'получаем массив с одним элементом
   ...
'пример сканирования всего массива для доступа к его данным
Dim U As Long
Dim L As Long
L = LBound(Store)
U = UBound(Store)
For i = L To U
 If (Store(i).Name = sname) Then
   ...
   Exit For
 End If
Next i
   ...
'пример добавления в массив одного элемента с сохранением 
' данных существующих элементов массива
U = UBound(Store)
ReDim Preserve Store(U + 1)
Store(U).Name = sname
Store(U).Summa = 0
Store(U).Kol = 0
   ...

Массивы можно присваивать друг другу (с копированием содержимого), как обычные переменные. Это, безусловно, удобно, однако (опять...) если массивы имеют разные типы данных, размерность, а также если один статический, а другой динамический, то возникает куча тонкостей, цитировать и помнить которые нет сил. Любителей отсылаю к спецлитературе.

6. Типы, задаваемые пользователем (собственные).

Определение общего (Public) собственного типа данных возможно только в секции General Declarations модуля (по-русски - в начале исходного файла, где задаём глобальные переменные). В этом случае этот тип будет доступен во всех процедурах всех форм, модулей и модулей классов. Для определения пользовательского типа в форме или модуле класса следует использовать ключевое слово Private, поскольку объявление общего типа в данной ситуации не допускается. При этом область видимости такого типа будет ограничена тем контейнером, где он был объявлен. Определив собственный тип данных, вы можете использовать его для объявления переменных этого типа (они могут быть, как обычно, локальными, глобальными или переменными контейнера).

[Private|Public] Type Имя_типа
   Элем1 [([Размерность])] As Тип
   Элем2 [([Размерность])] As Тип
   ...
End Type

Пример:

(General)(Declarations)(Module)
Type usrType
    Num As Long
    Name As String
    Price As Currency
End Type
...
(General)(Declarations)(Form)
Dim usrTools As usrType
Private Sub Command1_Click()
    usrTools.Name = "Отвёртка"
    usrTools.Price = 2.95
End Sub
...

7. Константы.

Область видимости может быть та же, что и у переменных (они могут быть локальными, глобальными или контейнера). Глобальная константа определяется с Public и её можно определить только в модуле.

[Public|Private] Const Имя_константы [As Тип] = Значение

8. Замечания по поводу процедур обработки событий:

- имя процедуры обработки событий составляется как "имяэлементауправления_имясобытия".
- при удалении элемента управления связанная с ним процедура становится общей.
- если создать элемент управления с тем же именем, что и удалённый, то все процедуры событий старого элемента привязываются к вновь созданному.
- добавление процедуры обработки события производится так - из выпадающего списка (Object) выбираем имя объекта, и из выпадающего списка (Procedure) выбираем требуемое событие.

9. Процедуры бывают общего назначения (глобальные Public), их можно вызвать из процедур другого контейнера и закрытые (Private, то есть принадлежащие определённому контейнеру - форме, модулю, классу). Закрытые процедуры доступны только изнутри контейнера. Начиная с VBasic 4.0, все процедуры обработки событий формы являются по умолчанию Private, то есть их можно вызвать только изнутри этой формы. Общие процедуры формы или модуля также являются закрытыми, причём они останутся закрытыми даже после того, как вы их объявите как Public. Тем не менее, из процедуры одной формы есть возможность вызвать процедуру другой формы - надо делать вызов в виде имя_формы.имя_процедуры_этой_формы. Внутри модуля полностью работают директивы Public и Private, причём Public указывать не обязательно, то есть все глобальные процедуры и переменные модуля являются по умолчанию общими (доступными из других модулей). Для того, чтобы сделать модуль закрытым для других приложений и проектов, есть выражение

Option Private Module

Это выражение делает составные элементы модуля (переменные, процедуры, функции, пользовательские типы данных и проч.), не объявленные как Private, доступными только для модулей проекта, но не для других проектов и приложений. В модуле выражение Option Private Module должно находиться перед самой первой процедурой (в начале модуля).

10. Аргументы.

Могут передаваться по ссылке (ключевое слово ByRef, которое можно опустить, поскольку такой тип передачи используется по умолчанию - не так, как в C и Pascal. Внимание! Не испортите случайно передаваемые переменные, при необходимости применяйте ByVal) или по значению (ключевое слово ByVal). При вызове можно использовать именованные аргументы, например:

Sub S(arg1, arg2, arg3)
   ...
End Sub
...
S 1, 2, 3                  'обычный вызов с неименованными параметрами
Call S (1, 2, 3)           ' то же самое
S arg2:=2, arg1:=1, arg3:3 'использование именованных параметров

При использовании именованных аргументов их можно передавать в любом порядке. Если при вызове использовать не все аргументы, то возникает сообщение об ошибке. Однако есть возможность объявить некоторые аргументы как необязательные, для этого перед именем аргумента ставится ключевое слово Optional. После первого необязательного аргумента все остальные тоже должны быть необязательными. Внутри функции можно проверить, был ли передан аргумент с помощью специальной функции IsMissing (см. help).

11. Циклы.

Вроде ничего особенного, за исключением того, что цикл while условие wend считается в VBasic "нестандартным", и для него нет оператора досрочного выхода из цикла Exit.

12. Существует такое понятие, как массив элементов управления (ЭУ), например, кнопок. Чтобы создать массив ЭУ, достаточно скопировать в буфер обмена элемент и потом снова вставить его в форму, оставив его имя тем же. ЭУ в массиве имеют одно и то же имя и обработчики событий, но разное значение свойства Index. Значение этого свойства передаётся в обработчик события, что бывает очень удобно. С помощью операторов Load и Unload во время выполнения можно создавать на форме новые ЭУ. Более широкие возможности по созданию ЭУ runtime предоставляет оператор Add.

13. Для работы с реестром есть функции (установки программ на VBasic сохраняются в HKEY_CURRENT_USER\Software\VB and VBA Program Settings и дублируется в HKEY_USERS\S-1-5-21-1864085942-2410699049-2459454283-1003\Software\VB and VBA Program Settings):

   SaveSetting Имя_приложения, Секция, Ключ, Установка
   GetSetting Имя_приложения, Секция, Ключ[, Значение_по_умолчанию]
   GetAllSettings Имя_приложения, Секция
   DeleteSetting Имя_приложения, Секция[, Ключ]

Значение Имя_приложения не имеет значения, но для пользователя было бы удобнее, чтобы оно носило осмысленный характер (для облегчения поиска по реестру).

14. Кроме обычных, очевидных свойств, элементы управления имеют свойства Parent (которое позволяет считывать свойства родительского элемента-контейнера, например, формы) и Container (позволяет менять контейнер для ЭУ). Следующие ЭУ могут служить контейнером для других ЭУ - Form, Frame, Picture, Toolbar.

15. События клавиатуры - Keypress, KeyUp, KeyDown. Обычно событие вызывается для активного элемента управления. Если свойство KeyPreview установлено в true, то событие, связанное с клавиатурой, передаётся сначала форме, а затем текущему элементу управления. Оператор SendKeys позволяет записывать коды нажатий в буфер клавиатуры, тем самым имитируя клавиатурные нажатия.

16. Интересные свойства формы:

ControlBox определяет, отражается или нет системное меню, с помощью которого пользователь может выйти из программы (Alt+F4). Если системное меню удаляется, следует обеспечить пользователю другой способ выхода из программы.
MaxButton если false, то кнопка максимизирования в правой части окна удаляется, и из системного меню удаляется пункт Maximize.
MinButton... по аналогии ...

17. Для передачи фокуса приложению существует оператор AppActivate. Его первый параметр должен в точности совпадать с заголовком активируемой программы. Следующий пример запускает калькулятор Windows, складывает числа 1 и 2, передаёт результат в буфер обмена и закрывает калькулятор:

Private Sub Command1_Click()
   ret = Shell ("calc.exe", vbNormalFocus)
   AppActivate "Калькулятор", False
   SendKeys "1+2=^C%(F4)", True
   Text1.Text = Clipboard.CetText()
End Sub

18. Принцип работы с файлами в VBasic несколько необычен. Он осуществляется в такой последовательности:

- получение свободного дескриптора файла - handle (с помощью FreeFile)
- открытие файла путём связывания полученного handle с дисковым именем файла (Open для произвольного доступа, Input, Output и Append для последовательного доступа)
- чтение или запись данных (LineInput#, input$, print#, write#, put, Get)
- закрытие файла и связанного с ним handle (оператор Close)

19. Для печати используют 2 способа:

- оператор PrintForm (выводит всё содержимое формы без заголовка и рамки)
- объект Printer

20. Перехват ошибок времени выполнения (runtime).

Все ошибки периода выполнения разделяются на 2 вида - ожидаемые и неожиданные. Подготовка обработки ожидаемых ошибок осуществляется в 3 этапа:

- подготовка перехвата;
- проверка и устранение ошибки;
- продолжение выполнения программы.

Подготовка перехвата осуществляется оператором

On Error {Goto label|Resume Next|Goto 0}

При возникновении ошибки внутри блока On Error выполнение программы не прерывается и стандартное сообщение об ошибке не выдаётся, управление при ошибке передаётся на метку label, которая должна находиться в той же процедуре, что и оператор On Error. Обычно блок, начинающийся с метки label, находится в конце процедуры, и перед меткой label стоит оператор Exit (при безошибочном выполнении код после label не выполняется). Если в качестве метки указан 0, то снова включается стандартный режим обработки ошибок.

Второй этап заключается в выявлении типа ошибки. Для этого в VBasic существует объект Err, у которого свойство Number содержит код последней ошибки, а Description - текст её системного описания.

Завершив обработку ошибки, на третьем этапе следует продолжить нормальное выполнение программы. Оператор Resume позволяет продолжить выполнение со строки, вызвавшей ошибку, а Resume Next - со строки, следующей за строкой с ошибкой. Оператор Resume Next можно использовать вместе с оператором On Error, и тогда каждая строка, вызвавшая ошибку, игнорируется:

On Error Resume Next

Однако такая простейшая обработка ошибок не позволяет определить источники ошибки и исправить её.

Типичным примером использования обработчика ошибок может быть обработка нажатия кнопки Cancel элемента диалога CommonDialog. Если установить свойство CommonDialog.CancelError=true, то при щелчке на кнопке Cancel возникает ошибка. Если значение CancelError==false, то различить, какая из кнопок была нажата - Ok или Cancel - будет затруднительно. Пример:

Function OpenFile As String 
   On Error Goto Cancel
   CommonDialog.CancelError = true
   CommonDialog.Filter = "Все файлы (*.*)|*.*"
   CommonDialog.ShowOpen
   OpenFile = CommonDialog.Filename
Exit Function
Cancel:
   If Err.Number=cdlCancel Then 
      OpenFile = ""
      MsgBox Err.Description
      Exit Function
   Else
      MsgBox Err.Description
      Stop
   End If
End Function

Можно генерировать ошибки искусственно, вызвав Err.Raise код_ошибки. Например,

Err.Raise 11

генерирует ошибку деления на ноль.

21. Строки можно объединять (склеивать) оператором + или &.

22. Внимание! Если изменить имя объекта (например, кнопки), то все обработчики событий этого объекта перестают работать, поскольку они привязаны к старому имени. Поправить можно, поменяв соответственно новому имени все названия обработчиков событий объекта.

23. Интересные (необычные) отличия функций от процедур в VBasic:

- у функции аргументы всегда передаются в скобках.
- у процедур аргументы могут передаваться без скобок (кто бы мог подумать). Если же нам обязательно захотелось в процедуре засунуть аргументы в скобки, то процедуру нужно вызывать через слово CALL.

24. Кое что из "горячих клавиш".

F2 показать браузер объектов (что-то типа мини-справочника по объектам)
F4 показать диспетчер свойств объекта
Tab, Shift-Tab в текстовом редакторе перемещает текст (блок) вправо, влево на позиции табуляции
Ctrl-I Quick Info - отображает синтаксис переменной, процедуры, метода и т. п.
Shift-F9 Quick Watch
Ctrl-L Call Stack - отображает стек вызова процедур.
Ctrl-F8 Run To Cursor
F8 Step Into - шаг с заходом во все вложенные процедуры
Shift-F8 Step Over - трассировка только текущей процедуры (без захода во вложенные).
Ctrl-Shift-F8 Step Out - выполнить текущую процедуру до конца и попасть в точку её вызова.
F9 Toogle BreakPoint

25. Если вы по недосмотру не установили для Visual Basic 6.0 сервис-пак 3, то не удивляйтесь, что всё глючит не по-детски (например, при попытке скомпилировать простейший проект и получить exe-файл среда разработки неприлично рушилась).

26. OCX означает "OLE Custom Control".

27. В других проектах можно использовать некоторую составную часть другого проекта, например - модуль, форму и т. д. Для этого их просто копируют в рабочую папку проекта и добавляют в проект командой Project\Add... .

28. Очень полезна опция, запрещающая неявное определение переменных. Махом избавляемся от кучи ошибок. Для активизации надо:

- либо указать в начале модуля (это так называемая область General Declarations) строку Option Explicit
- либо через Tools\Options... закладка Editor установить галочку Require Variable Declaration.

29. Exit применяют в следующих контекстах:

Exit Do
Exit For
Exit Function
Exit Property
Exit Sub

Для выхода из процедуры нельзя использовать GoSub, GoTo, или Return.

30. Для преобразования числа в строку применяют Str, Hex (хорошо подходят для целых чисел) и Format (для чисел с плавающей запятой и многих других типов). Format довольно гибко позволяет форматировать выходные данные. Вот пример обработки функцией Format даты и времени:

'
' Автоматическое генерирование имени файла из текущих даты и времени.
'
Function FileName() As String
    Dim currentTimeDate As Variant
    Dim iYear, iMonth, iDay As Integer
    Dim iHour, iMinute, iSec As Integer
    
    currentTimeDate = Now()
    iYear = Year(currentTimeDate)
    iMonth = Month(currentTimeDate)
    iDay = Day(currentTimeDate)
    iHour = Hour(currentTimeDate)
    iMinute = Minute(currentTimeDate)
    iSec = Second(currentTimeDate)
    FileName = Format(currentTimeDate, "yymmdd") _
             + Format(currentTimeDate, "hhmmss") + ".bmp"
End Function

31. Для преобразования строки в число применяют Val, (хорошо подходят для целых чисел) или, в зависимости от типа данных, одну из следующих функций:

CBool(expression)
CByte(expression)
CCur(expression)
CDate(expression)
CDbl(expression)
CDec(expression)
CInt(expression)
CLng(expression)
CSng(expression)
CStr(expression)
CVar(expression)

32. Для чтения текстового файла в VBA (Visual Basic for Application) есть, по крайней мере, 3 способа - использование CreateObject("Scripting.FileSystemObject"), object.OpenAsTextStream([iomode, [format]]) и импорт в таблицу Excel. Пример использования CreateObject("Scripting.FileSystemObject") для считывания файла exclude.txt в динамический массив:

Dim ExceptionsFN As String
Dim Exclusions() As String
  
'считываем файл исключаемых магазинов
Path = ActiveWorkbook.FullName
Path = Mid(Path, 1, Len(Path) - Len(Dir(Path)))
ExceptionsFN = Path + "exclude.txt"
Dim FS As Object
Dim tf As Object
Dim LineCnt As Integer
Set FS = CreateObject("Scripting.FileSystemObject")
Set tf = FS.OpenTextFile(ExceptionsFN)
LineCnt = 0
Do
   If tf.AtEndOfStream Then
      Exit Do
   End If
   ReDim Preserve Exclusions(LineCnt)
   Exclusions(LineCnt) = tf.ReadLine
   LineCnt = LineCnt + 1
Loop
tf.Close

33. VBA Excel: как обработать в цикле по столбцам и строкам все ячейки выделенной области?

Для доступа к выделению служит объект Selection. Для доступа к столбцам выделения служит дочерний объект Selection.Columns, а к строкам Selection.Rows. И Columns, и Rows имеют свойство Count, которое дает количество столбцов и строк соответственно. Пример заполнения выделенной области символом "A":

Dim xCount, yCount
     
xCount = Selection.Columns.Count
yCount = Selection.Rows.Count
For x = 1 To xCount
    For y = 1 To yCount
        Selection.Cells(y, x).Value = "A"
    Next
Next

34. VBA Excel: почему при попытке присвоить цвет через Interior.ColorIndex происходит ошибка выполнения 1004 (невозможно установить свойство ColorIndex класса Interior)?

Run-time error '1004':
Unable to set the ColorIndex property of the Interior Class

Пример кода, который дает такую ошибку:

Dim clYellow As Long
 
clYellow = RGB(&HFF, &HFF, &H0)
'На этой строке ошибка Run-time error '1004':
MyRange.Cells(1, 2).Interior.ColorIndex = clYellow

Этот код может нормально работать в Excel 2010, и давать ошибку в Excel 2003, причем нерегулярно. Причина в том, что Microsift поменяло реализацию основного поведения методов VBA, так что это может повлиять на то, как работает код в разных версиях Excel. Если у Вас нет возможности в данной ситуации обновить версию Office, то используйте вместо присвоения свойства ColorIndex свойство Color.

'Теперь проблемы с установкой цвета нет:
MyRange.Cells(1, 2).Interior.Color = clYellow

35. VBA: как открыть диалог выбора файла?

Dim lngCount As Long

With Application.FileDialog(msoFileDialogOpen)
     ' В свойство InitialFileName можно записать путь, от которого
     ' стартует диалог, например "c:\temp". В этом примере берется
     ' путь текущего каталога, где находится файл документа Office.
    .InitialFileName = Application.ActiveWorkbook.Path
    ' Разрешить выбрать несколько файлов сразу:
    .AllowMultiSelect = True
    ' Если это надо, добавим фильтр по расширению файла:
    .Filters.Add "Bitmap", "*.bmp; *.gif; *.jpg; *.jpeg", 1
    ' Отобразить диалог:
    .Show
    ' Отобразить полный путь до каждого выбранного файла
    For lngCount = 1 To .SelectedItems.Count
        MsgBox .SelectedItems(lngCount)
    Next lngCount
End With

[Ссылки]

1. Разработка GUI с помощью Excel.