Visual2000 · Архив статей А.Колесова & О.Павловой

Советы тем, кто программирует на Visual Basic и MS Office/VBA

Андрей Колесов

© 1996, Андрей Колесов
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" N 3/96, c. 62-64.

Когда в январе 1996 года я отправлял эту статью в "КомпьютерПресс", то никак не думал, что будет написан даже четвертый совет. Сегодня (май 2000 года) их число уже приблизилось к трехстам...


Совет 1. Используйте содержательные имена объектов

При создании нового объекта (формы или элемента управления) ему автоматически присваивается идентификатор (свойство Name), который состоит из имени, назначаемого по умолчанию, и порядкового номера объекта данного типа. Для элементов управления нумерация ведется в пределах каждой отдельной формы, а для формы — в рамках текущего проекта.

Однако при создании формы с достаточно большим числом объектов, особенно одного типа, гораздо удобнее вместо безликих названий наподобие Command1, Command2, Command3 использовать более содержательные имена, например, Exit, OK, Cancel.

Изменение имен объектов выполняется с помощью соответствующих установок свойства Name в окне Properties.

Если для элементов управления изменение имен является, в общем-то, необязательным делом, то для форм, используемых в различных проектах, установка уникальных идентификаторов является просто необходимой. При попытке загрузки сделанной ранее формы Form2 в новый проект, в котором уже создана форма с таким же именем, будет, естественно, выдаваться сообщение о невозможности этой операции.

Здесь следует иметь в виду одно важное обстоятельство. Дело в том, что имя элемента управления автоматически используется в формировании относящихся к нему событийных процедур, задействованных в данном модуле формы. (Для самой формы — она одна в модуле — используется фиксированное имя Form независимо от значения свойства Name.) Поэтому, создав, например, командную кнопку Command1, а затем описав для нее обработку события "щелчок мышью", вы получите процедуру с названием Command1_Click. Если же после этого вы решите изменить идентификатор кнопки, то получится так, что ее ранее задействованные событийные процедуры останутся в этом модуле со старыми названиями. Причем все эти процедуры превратятся просто в мусор, так как они никак не будут использоваться: для данной кнопки нужно будет заново описывать все процедуры или менять их старые имена на новые.

То же самое относится и к идентификаторам свойств объекта, с той лишь разницей, что при запуске программы будет сразу выдано сообщение об ошибке — "variable not defined", что в данном случае означает "неверный идентификатор свойства". Поэтому при изменении имени элемента управления рекомендую воспользоваться таким советом:

Совет 1.1. При изменении имени объекта сделайте коррекцию идентификаторов всех соответствующих ему процедур и свойств в коде модуля с использованием команды Replace в меню Edit (например, заменить "Command1" на "cmdExit").

Конечно, было бы гораздо удобнее, если бы такая модификация выполнялась автоматически при изменении имени элемента управления, — это явное упущение разработчиков языка. И в связи с этим еще одна очевидная рекомендация.

Совет 1.2. Присваивайте собственное уникальное имя элементу управления сразу после его создания и в будущем старайтесь не изменять его без особой нужды. Помните, что эти идентификаторы могут использоваться и в других модулях программы для адресации к свойствам и методам данных объектов.

Разумеется, при присвоении нового идентификатора объекта желательно отразить в имени не только его функциональное назначение, но и его тип. Для этого лучше всего использовать в именах объекта префиксы (приставки), предлагаемые разработчиками VB в качестве неформального стандарта (см. таблицу), которого стараются придерживаться все пользователи языка.

Таблица. Соглашение об именах объектов (для стандартного набора объектов)

Тип объекта Название объекта (свойство Name)
По умолчанию Префикс Пример
Form Form Frm frmFileOpen
Check box Check Chk chkReadOnly
Combo box Combo Cbo cboRussian
Command button Command Cmd cmdCancel
Data Data Dat datBiblio
Directory list box Dir Dir dirSource
Drive list box Drive Drv drvTarget
File list box File Fil filSource
Frame Frame Fra fraLanguage
Grid Grid Grd grdPrices
Horizontal scroll bar HScroll Hsb hsbVolume
Image Image Img imgIcon
Label Label Lbl lblHelpMessage
Line Line Lin linVertical
List box List Lst lstPolicyCodes
Menu Menu Mnu mnuFileOpen
OLE OLE Ole oleObject1
Option button Option Opt optRussian
Picture box Picture Pic picDiskSpace
Shape Shape Shp shpCircle
Text box Text Txt txtGetText
Timer Timer Tmr tmrAlarm
Vertical scroll bar VScrll Vsb vsbRate

Удобство применения именно префиксов заключается и в том, что список объектов будет автоматически упорядочен по типам объектов. Обратите внимание, что при написании префикса предлагается использовать только строчные (маленькие буквы) — это позволяет сразу отличить стандартные имена от присвоенных пользователем. Применение данного соглашения поможет вам не запутаться в собственных программах, и самое главное — предоставит другим пользователям ваших программ возможность легко ориентироваться в них.

В начало статьи

Совет 2. Оптимизируйте время загрузки нового проекта

При создании нового проекта (команда New File) в качестве начального списка дополнительных элементов управления (VBX), включаемых в окно ToolBox, используется файл AUTOLOAD.MAK (он хранится в главном каталоге самого пакета VB). Эта операция выполняется автоматически каждый раз при запуске среды VB.

При инсталляции VB в файл AUTOLOAD.MAK записываются все дополнительные VBX'ы, входящие в состав данной редакции. Для VB 3.0 Professional их число равно четырнадцати, и время их загрузки даже для 486-го компьютера довольно ощутимо (несколько секунд), хотя абсолютное большинство этих модулей для данного проекта не нужно.

Совет 2.1. Удалите вообще файл AUTOLOAD.MAK (имеется в виду, конечно, — сохраните под другим именем, так как начальный список VBX'ов может пригодиться). Команда New File будет выполняться мгновенно, при этом на экране вы получите минимальный набор элементов управления, который можно будет расширять по мере необходимости с помощью команды Add File".

Совет 2.2. Если вы определились с составом дополнительных элементов управления для своих проектов, то соответственно отредактируйте содержимое файла AUTOLOAD.MAK, удалив из него неиспользуемые строки и добавив свои собственные.

Совет 2.3. Можно сделать несколько собственных начальных заготовок MAK-файлов, создавая новый проект с помощью команды Open Project, а потом сохраняя его под оригинальным именем командой Save As.

Совет 2.4. Если вы долго работаете с одним и тем же проектом, присвойте ему имя AUTOLOAD.MAK, и тогда при запуске VB он будет загружаться сразу.

В начало статьи

Совет 3 (глобальный). Используйте функции Windows API

Одним из эффективных средств расширения возможностей Visual Basic является использование многочисленных функций Windows API (Application Program Interface — программный интерфейс приложений Windows). При этом есть два основных аспекта применения Windows API:

Наверное, вполне возможно такое сравнение: Windows API для систем программирования в Windows являются примерно тем же, чем для DOSовских систем являются системные функции BIOS/DOS. Отличие заключается в том, что состав функций Windows API гораздо шире (с их помощью, кстати, можно обращаться и к функциям BIOS/DOS) и обращение к ним выполняется с помощью обыкновенных процедурных обращений.

Однако нужно подчеркнуть, что использование функций API имеет и обратную сторону. Как известно, одной из самых сильных черт программирования в среде является надежность создаваемых программ: работая под управлением интерпретатора, они теоретически не могут нарушить работу Windows. Но использование напрямую функций Windows API или других DLLбиблиотек снимает эту защиту (основная проблема — отсутствие контроля за межпроцедурным интерфейсом). Поэтому ошибка в обращении к внешним функциям может привести к неработоспособности системы, что особенно актуально на этапе разработки программы.

Все это требует очень аккуратной работы при использовании функции API. По данному поводу в зарубежной специальной литературе есть целый ряд полезных рекомендаций. Но стоит отметить, что эта проблема аналогична общей постановке вопроса смешанного программирования и использования двоичных библиотек. Данный вопрос, в частности, на примере Basicпрограммирования в среде DOS рассматривался в нескольких номерах журнала "Монитор" — ьь 8'94, 1-2'95.

В состав VB 3.0 входят специальные справочные файлы, посвященные функциям Windows API. Кроме того, файл WIN31API.HLP содержит готовые операторы объявления для функций API, которые можно просто скопировать в текст разрабатываемой программы. В файле WIN31WH.HLP приведено подробное описание всех функций, расположенных в алфавитном порядке имен, поэтому справкой легко пользоваться, если вам известно имя функции. Но если вы хотите посмотреть, есть ли какая-нибудь подходящая функция для решения возникшей проблемы, то сделать это совсем непросто. Аналогичные справочные средства по Windows 95 API имеются в составе VB 4.0.

Следует иметь в виду, что функции API в Windows 95 представляют собой не простое расширение состава аналогичных функций для Windows 3.x. В Windows 95 API изменен интерфейс обращения ко многим "старым" функциям, поэтому в общем случае при переходе от Windows 3.x к Windows 95 потребуется аккуратная коррекция исходного текста программы.

Всем, кто хотел бы более детально познакомиться с проблемой использования функций Windows API в среде Visual Basic, полезно познакомиться с книгой известного американского автора Daniel Appleman "Visual Basic Programmer's Guide for Windows API", которая в течение последних двух-трех лет постоянно входит в число бестселлеров по теме "Visual Basic". В начале книги приводится одно их самых лучших объяснений, что такое Windows и что такое программирование в Windows. Далее дается описание всех 700 функций Windows API, которые разбиты на логические функциональные группы с точки зрения VB-программиста. В начале 1996 года ожидается выход новой книги Appleman на эту же тему для VB 4.0 и Windows 95.

В начало статьи

Совет 3а. Как реализовать в VB/Win функции MKx$/CVx

Для многих из тех, кто ранее программировал в DOSовских версиях MS Basic (Quick, PDS, Visual), при переходе в VB/Win неприятным сюрпризом стало отсутствие в нем целой группы очень полезных встроенных функций MKx$/CVx (см. описание MS Basic/DOS). С их помощью производится преобразование числовых (целых, вещественных и пр.) данных в строковый формат, и наоборот. Точнее говоря, никакого преобразования значений здесь нет, а просто N-е количество байт меняет название типа данных.

Основной смысл такого преобразования — возможность хранения и передачи разнотипных данных в виде одной строковой переменной. Это является отличной альтернативой структурам данных с их жестким описанием полей на уровне исходного текста. Обсуждение возможностей использования таких конструкций вполне достойно отдельного разговора, поэтому приведем только один простой пример.

Пример. Необходимо представить адрес смещения в файле, выраженный переменной Offset& (тип LONG), в виде двух беззнаковых двухбайтовых целочисленных переменных. Это часто бывает необходимо для обращения к функциям BIOS/DOS6 и проблема заключается в том, что в MS Basic нельзя использовать беззнаковые переменные в арифметических операциях. Один из простых способов решения этой проблемы выглядит так:

' Offset& => HighOffset% & LowOffset%
StringValue$ = MKL$(Offset&)
HighOffset% = CVI(LEFT$(StringValue$, 2))
LowOffset% = CVI(RIGHT$(StringValue$, 2))
'
' HighOffset% & LowOffset% => Offset%
Offset& = CVL(MKI$(HighOffset%) + MKI$(LowOffset))

Однако в VB/Win можно довольно просто реализовать эти полезные Basic-функции с помощью Windows API (хотя лучше, если бы они уже были созданы). Набор таких процедур приведен в модуле MKXCVX.BAS, а пример их применения — в модуле MKXCVXTS.BAS. Они используют очень полезную функцию HMemCpy, которая копирует заданное число байт из одной области памяти в другую. Имеет смысл взять данную функцию на заметку — она может пригодиться и для других операций (например, для быстрого копирования одного массива в другой).

Здесь следует обратить внимание прежде всего на то, что в VB/Win, по сравнению с Basic/DOS, несколько изменилось назначение и формат оператора Declare:

  1. В VB/Win этот оператор используется только для обращения к внешним процедурам, представленным в виде DLL-библиотек, и API-функциям (аналог в DOSовских версиях — QLB-библиотеки). В Basic/DOS оператор Declare был нужен и для процедур, загруженных в виде исходных модулей (для процедур-подпрограмм он мог формироваться автоматически, а для функций его надо было писать вручную). Именно поэтому в модуле MKXCVXTS.BAS отсутствуют объявления функций MKx/CVx.

  2. В Basic/DOS для одной программы можно было использовать только одну QLB-библиотеку, имя которой указывалось при запуске программы. Для VB/Win-программы может использоваться много различных внешних библиотек. Поэтому в операторе Declare появилось описание имени файла, в котором находится описываемая функция.

Теперь хотелось бы сделать одно замечание. С точки зрения разработчика механизм, реализованный в VB/Win, вроде бы достаточно удобен. Но для работы готовой программы он не оптимален. Представьте себе, если вы используете 10 маленьких процедур, каждая из которых находится в отдельной (и очень большой) DLL-библиотеке. Совершенно очевидно, что это не лучшим образом отразится на быстродействии программы или на требованиях к оперативной памяти. Было бы гораздо удобнее, если бы был реализован традиционный принцип создания исполняемого модуля: выборка на этапе компоновки из двоичных библиотек только нужных процедур и включение их в EXE-модуль или формирование из них оптимальной DLL-библиотеки для данной задачи.

'================================================
' Модуль MKXCVX.BAS
'================================================
'
'Реализация функций CVx/MKx$ с помощью обращения к
'функции Windows API HmemCpy (копирование заданного
'числа байт из одной области памяти в другую)
'
'ПРИМЕЧАНИЕ. Передача строковой переменной по значению
'(ByVal) означает, что в функцию HMemCpy передается
'адрес не описателя, а самой строки
'
Declare Sub HMemCpy Lib "kernel" _
  (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
'
Function CVD (x$) As Double
  HMemCpy Temp#, ByVal x$, 8
  CVD = Temp#
End Function

Function CVI (x$) As Integer
  HMemCpy Temp%, ByVal x$, 2
  CVI = Temp%
End Function

Function CVL (x$) As Long
  HMemCpy Temp&, ByVal x$, 4
  CVL = Temp&
End Function

Function CVS (x$) As Single
  HMemCpy Temp!, ByVal x$, 4
  CVS = Temp!
End Function

Function MKD$ (x#)
  Dim Temp As String * 8
  HMemCpy ByVal Temp, x#, 8
  MKD$ = Temp
End Function

Function MKI$ (x%)
  Dim Temp As String * 2
  HMemCpy ByVal Temp, x%, 2
  MKI$ = Temp
End Function

Function MKL$ (x&)
  Dim Temp As String * 4
  HMemCpy ByVal Temp, x&, 4
  MKL$ = Temp
End Function

Function MKS$ (x!)
  Dim Temp As String * 4
  HMemCpy ByVal Temp, x!, 4
  MKS$ = Temp
End Function

'================================================
' Модуль MKXCVXTS.BAS
'================================================
'
Sub main ()
'
'Пример обращения к функциям MKx$, CVx
'в стиле a la MS Basic for DOS (QB/PDS/VBDOS)
'
  IntValue% = 12345
  x$ = MKI$(IntValue%)
  MsgBox Str$(CVI(x$))
  '
  LongValue& = 12345678
  x$ = MKL$(LongValue&)
  MsgBox Str$(CVL(x$))
  '
  SingleValue! = 123.45
  x$ = MKS$(SingleValue!)
  MsgBox Str$(CVS(x$))
  '
  DoubleValue# = 1.2345
  x$ = MKD$(DoubleValue#)
  MsgBox Str$(CVD(x$))
  '
End Sub

В начало статьи