Советы тем, кто программирует на Visual Basic
Совет 191. Как определять високосный год
Совет 192. Форматирование числа при выводе
Совет 193. Как измерять временные интервалы
Совет 194. Как узнать значения кодовых таблиц
Совет 195. Как запустить Web-браузер из VB-приложения
Совет 197. Создание Word-документов при помощи VB-кода
Совет 198. Разбор полей строковой переменной при помощи функции SPLIT
Совет 199. Создайте свою собственную функцию Format
Совет 200. Как осуществить выход из Windows с помощью VB
Совет 201. Для ускорения процесса загрузки изменяйте базовые адреса компонентов типа in-process
Совет 202. Зачем нужен параметр Alias в операторе Declare
Совет 203. Помните: VB использует кодировку ANSI при обращении к API-функциям
Совет 191. Как определять високосный год
Те, кто думают, что високосный год — это тот, который делится без остатка на четыре, глубоко заблуждаются. Так определяется високосный год в Юлианском календаре (старый стиль). А вот в Григорианском (новый стиль) — для устранения несоответствия календарного и солнечного (астрономического) года 100-й год не считается високосным, но каждый 400-й — считается. Ошибка средней продолжительности года в этом алгоритме составляет всего 26 секунд.
Таким образом, алгоритм определения високосного года выглядит следующим образом:
Public Function IsLeapYear(iYear As Integer) ' Проверка високосного года If (iYear Mod 4 = 0) And _ ((iYear Mod 100 <> 0) Or (iYear Mod 400 = 0)) Then IsLeapYear = True ' високосный Else IsLeapYear = False ' не високосный End If End Function
Но можно придумать алгоритм гораздо проще. Еще раз — что такое високосный год? Правильно, тот, который имеет дату 29 февраля. А значит, работает такой алгоритм:
Function IsLeap (sYear As String) As Integer IsLeap = False If IsDate (» 02/29/» & sYear) Then IsLeap = True End Function
Однако по нашему мнению, данный алгоритм определения високосного года можно применять только к Григорианскому календарю, который был введен в действие в 1582 году. А до этого момента следует использовать правило Юлианского календаря (подробнее об этом см. статью « Y2K: как вести календарь» в этом же номере).
![]() |
![]() |
Совет 192. Форматирование числа при выводе
Иногда бывает полезно выводить числовую информацию с фиксированным числом знаков, заполняя левые позиции нулями. Для этого можно воспользоваться следующей функцией:
Function PadToString(myValue, Digits) As String Dim Digits, MyValue PadToString = String(Digits - Len(myValue), _ " 0" ) & myValue End Function
Сделав, например, такое обращение
NewStr$ = PadToString(1978, 8)
вы получите строковую переменную 00001978. Обратите внимание, что Digits и myValue — переменные типа Variant.
![]() |
![]() |
Совет 193. Как измерять временные интервалы
Для измерения временных интервалов между двумя произвольными моментами времени удобнее всего использовать встроенную функцию:
DateDiff (Interval, StartDate, EndDate[, FirstDay,[ FirstWeek]])
Строковая переменная Interval задает единицы измерения интервала — от секунд до года. Однако часто при тестировании скорости выполнения различных программных конструкций (BenchMark) секундной дискретности бывает недостаточно. В этом случае лучше воспользоваться еще одними внутренними системными часами Windows, которые измеряют время в « тиках» — миллисекундах с момента последнего старта или перезагрузки операционной системы. (При непрерывной работе компьютера обнуление счетчика происходит примерно каждые 49 суток.) Чтение значений « тиков» в Win32 производится с помощью API-функции GetTickCount$ (в Win16 для этого использовалась функция GetCurrentTime$).
Пример вычисления временных интервалов в программе может выглядеть таким образом:
Private Declare Function GetTickCount& Lib « kernel32» () Private Sub Form_Load() Dim dStart As Date Dim lCount&, i&, strValue$, msec& ' lCount = 1000000 ' счетчик циклов dStart = Now msec& = GetTickCount& For i = 1 To lCount strValue = " Петя» & " +" & " Вася" Next MsgBox " Интервал: сек = " & _ DateDiff(» s" , dStart, Now) & _ " мсек = " & GetTickCount& - msec& End Sub
Однако следует иметь в виду, что вычисленный таким образом интервал не является «чистым» временем выполнения данного программного кода. В него, включается также время выполнения более приоритетных заданий операционной системы — работа других приложений, системных драйверов и пр. Для более точного определения « чистого» времени выполнения конкретного вычислительного процесса с точностью до 100 наносекунд в составе Win32 API системы Windows NT имеется функция GetProcessTime.
![]() |
![]() |
Совет 194. Как узнать значения кодовых таблиц
Как известно, Windows использует в своей работе две кодовые таблицы. Мы обычно называем их таблицами «DOS» и «Windows» , но формально они именуются «OEM» и «ANSI» . Довольно многие функции обработки строковых переменных зависят от значения кодовых таблиц, установленных при инсталляции системы, которые можно определить с помощью функций API:
Private Declare Function GetACP Lib _ " kernel32" () As Long Private Declare Function GetOEMCP Lib _ " kernel32" () As Long Private Sub Form_Load() MsgBox " ACP = " & GetACP & vbCrLf & _ " OEMCP = " & GetOEMCP End Sub
В принципе значения кодовых таблиц можно менять уже после инсталляции Windows. Мы не советуем злоупотреблять этим, но такая коррекция может быть полезной при тестировании и анализе работы программ. Параметры ACP и OEMCP хранятся в разделе HKEY_Local_Machine/System/CurrentControlSet/Control/Nls/Codepage файла Реестра и редактируются с помощью утилиты REGEDIT.EXE. Для их активизации нужно перезагрузить Windows.
Номера таблиц, которые могут понадобиться:
OEMCP = 866 (Russian), 437 (US — default), 850 (International), 855 (Cyrilic) ACP = 1250 (Eastern European), 1251 (Cyrilic & Russian), 1252 (US & Western European), 1200 (Unicode)
![]() |
![]() |
Совет 195. Как запустить Web-браузер из VB-приложения
Здесь приводится довольно простая функция, позволяющая запустить используемый по умолчанию Web-браузер из своего VB-приложения.
Введите следующий код в раздел General для модуля:
Declare Function ShellExecute Lib « shell32.dll" _ Alias " ShellExecuteA" _ (ByVal hwnd As Long, ByVal lpOperation As String, _ ByVal lpFile As String, ByVal lpParameters As String, _ ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long Public Const SW_SHOWNORMAL As Long = 1 Public Const SW_SHOWMAXIMIZED As Long = 3 Public Const SW_SHOWDEFAULT As Long = 10 Затем поместите в форму код, запускающий используемый по умолчанию Web-браузер: Public Sub RunBrowser(strURL As String, iWindowStyle As Integer) Dim lSuccess As Long '-- Shell to default browser lSuccess = ShellExecute(Me.hwnd, " Open" , _ strURL, 0&, 0&, iWindowStyle) End Sub
Для запуска какого-либо Web-узла, к примеру http://www.visual.2000.ru/, используйте такой вызов функции:
Call RunBrowser (" www.visual.2000.ru" , SW_SHOWNORMAL)
![]() |
![]() |
Совет 196. Чтобы избежать появления ошибок, преобразовывайте значения NULL в пустые строковые переменные
При получении значений NULL из объекта Recordset могут возникать ошибки. Одним из способов избежать такой ситуации является проверка значения поля, и в том случае, если оно равно NULL, преобразование его в пустую строковую переменную или в ноль. Например,
If isnull(rs(" Field" )) then tmp=" » else tmp=rs(" Field" ) form.textfield=tmp
Но еще проще использовать функцию форматирования, которая бы автоматически преобразовывала значение NULL в пустую строку. Такая функция выглядит примерно следующим образом:
form.textfield=format(rs(" Field" ))
![]() |
![]() |
Совет 197. Создание Word-документов при помощи VB-кода
Вы никогда не хотели создавать профессионального вида документы, наподобие тех, что пишутся в Microsoft Word, при помощи программного кода на VB? Следуйте приведенным ниже инструкциям, и у вас появится такая возможность.
Шаг 1. Добавьте к проекту ссылку к Microsoft Word 8.0 Object Library (команда Project|References).
Шаг 2. Введите следующий код для создания экземпляра Word и напишите какой-либо текст в новом документе:
Dim objWord As New Word.Application '-- Выводит Microsoft Word objWord.Visible = True '-- Добавляет новый документ objWord.Documents.Add '-- Вводит текст в документ objWord.Selection.TypeText " Visual Basic!" '-- Выделяет весь текст objWord.Selection.WholeStory '-- Изменяет размер шрифта objWord.Selection.Font.Size = 50 Set objWord = Nothing
Шаг 3. Внимательно изучите Object Browser, чтобы воспользоваться другими свойствами и методами, предоставляемыми объектом Word.
![]() |
![]() |
Совет 198. Разбор полей строковой переменной при помощи функции SPLIT
Процедуры для проведения синтаксического разбора являются наиболее часто обновляемыми среди всех функций, манипулирующими строковыми переменными. VB6 не стал исключением, и в его состав вошла новая функция SPLIT. Она очень проста в использовании, всего одна строка кода — и вы можете анализировать любую строку при помощи конкретного разделителя. Эта строка будет выглядеть следующим образом:
Dim strAnimals As String Dim iCounter As Integer Dim arrAnimals() As String strAnimals = " Cats,Dogs,Horses,Birds" '-- Синтаксический разбор строки arrAnimals = Split(strAnimals, " ," ) '-- Просмотр массива в цикле For iCounter = LBound(arrAnimals) To UBound(arrAnimals) MsgBox arrAnimals(iCounter) Next
![]() |
![]() |
Совет 199. Создайте свою собственную функцию Format
Команда Format, входящая в состав VB5, работает практически аналогично команде Print. Отличие состоит в том, что Format укорачивает выводимую строковую переменную, если количество символов форматирования превышает ее длину Для решения этой проблемы создадим свою функцию с названием FormatNum.
Public Function FormatNum(MyNumber As Double, FormatStr As String) ' Эта функция возвращает число, отформатированное как строковая 'переменная, содержащая требуемое минимальное количество символов ' ' MyNumber — Используйте CDbl(MyNumber) при вызове функции, чтобы ' избежать появления ошибки о несовпадении типов ' FormatNum = Format(MyNumber, FormatStr) If Len(FormatNum) < Len(FormatStr) Then FormatNum = Space(Len(FormatStr) - Len(FormatNum)) _ & FormatNum End If End Function
Использование новой функции Format проиллюстрируем на таком примере:
Private Sub Form_Load() Dim MyVar, FormatStr$, f1$, f2$ MyVar = 12.1 FormatStr$ = " ####.##» f1$ = Format$(MyVar, FormatStr$) f2$ = FormatNum(MyVar, FormatStr$) MsgBox f1$ & " " & Len(f1$) & vbCrLf & _ f2$ & " " & Len(f2$) End Sub
![]() |
![]() |
Совет 200. Как осуществить выход из Windows с помощью VB
Вам никогда не хотелось сделать так, чтобы ваши приложения могли автоматически выполнить операцию выхода из Windows? В этом нет ничего сложного, просто нужно использовать вызов соответствующей API-функции. Для этого добавьте следующее описание функции и константы в BAS-модуль:
Declare Function ExitWindowsEx& Lib _ » user32» (ByVal uFlags&, ByVal wReserved&) ' константы, необходимые для выхода из Windows Global Const EWX_FORCE = 4 ' закрытие неактивных приложений Global Const EWX_LOGOFF = 0 ' выход из системы Global Const EWX_REBOOT = 2 ' перезагрузка Global Const EWX_SHUTDOWN = 1 ' закрытие системы Для выключения Windows используйте такой вызов функции: ' выключает компьютер lresult = ExitWindowsEx(EWX_SHUTDOWN, 0&)
Для выполнения других операций замените первый параметр при вызове функции ExitWindowsEx на соответствующую константу.
![]() |
![]() |
Совет 201. Для ускорения процесса загрузки изменяйте базовые адреса компонентов типа in-process
При загрузке собственного компонента типа in-process в ходе выполнения VB-приложения, этот компонент размещается, начиная с некоторого базового адреса памяти.
Как можно поменять этот адрес для своего компонента? Для этого откройте диалоговое окно Project Properties и выберите вкладку Compile. Адрес вводится в поле DLL Base Address в виде десятичного или шестнадцатеричного целого числа без знака. По умолчанию используется значение &H11000000 (285,212,672). Если вы забудете изменить его, ваш компонент вступит в противоречие с любым другим компонентом типа in-process, скомпилированным с учетом используемого по умолчанию адреса. Поэтому рекомендуем вам задавать какой-либо отличный от него адрес.
Выбирайте базовый адрес в диапазоне между 16 Мбайт (16 777 216 или &H1000000) и 2 Гбайт (2,147,483,648 или &H80000000). При этом он должен быть кратным 64 Кбайт. Область памяти, используемая вашим компонентом, начинается с исходного базового адреса и имеет размер скомпилированного файла, округленного до следующего числа, кратного 64 Кб.
Ваша программа не может превышать 2 Гбайт, поэтому максимальный базовый адрес фактически равен 2 Гбайт минус область памяти, занимаемая созданным компонентом. Исполняемые файлы обычно будут загружаться по логическому адресу в 4 Мбайт. Область памяти меньше 4 Мбайт резервируется для Windows 95, а области свыше 2 Гбайт — для Windows 95 и Windows NT.
Чтобы убедиться, что все ваши компоненты имеют различные базовые адреса, необходимо вести их учет. Для этого полезно разработать собственный инструмент, который создавал бы новые уникальные базовые адреса. Таким образом ваши компоненты не вступали бы в противоречие друг с другом.
![]() |
![]() |
Совет 202. Зачем нужен параметр Alias в операторе Declare
Издавна (еще в версиях для DOS) в операторе Declare использовался ключ Alias для переопределения имени вызываемой процедуры. Конструкция
Declare Function vbname Lib libname [Alias aliasname]
означает, что к процедуре, записанной в библиотеке libname под именем aliasname, в VB-программе обращаются под именем vbname. Обычно это применяется в двух случаях:
- Когда имя aliasname просто недоступно в VB. В частности, при обращении к функциям, которые имеют (по принятым в C правилам) в начале названия символ подчеркивания, например _lOpen. Или когда внешняя функция использует имя, которое совпадает с зарезервированным ключевым словом VB (скажем SetFocus).
- Когда подразумевается, что VB-программа может работать с двумя версиями одной и той же (с точки зрения функциональности) процедуры. В этом случае гораздо проще исправить один оператор Declare:
Declare Function vbname Lib libname Alias aliasname1
на
Declare Function vbname Lib libname Alias aliasname2
чем менять во всей программе имя процедуры vbname.
Второй вариант особенно часто используется для обеспечения преемственности кода при переходе от Win16 к Win32 — имена многих API-функций при этом поменялись. В этой связи нужно специально выделить часто встречающийся случай, когда к привычному имени, типа SomeFunction (Win16), прибавился суффикс A и получилось SomeFunctionA (Win32). Почему так случилось, расскажет следующий совет.
![]() |
![]() |
Совет 203. Помните: VB использует кодировку ANSI при обращении к API-функциям
Дело в том, что Windows 3.x (Win16) использовала только один формат хранения символьных данных (строковых переменных) — ANSI (набор однобайтовых символов). Система Win32 представлена двумя вариантами — Windows 9x и Windows NT, которые применяют разные внутренние форматы: соответственно ASNI и Unicode (набор целочисленных, двубайтовых символов). В результате, одна API-функция в Win32, например GetWindowText, фактически реализована в двух вариантах, с добавлением к имени суффикса A (символы передаются в кодировке ANSI) и W (Wide = Unicode): GetWindowTextA и GetWindowTextW. При этом следует иметь в виду, что физически в Windows 9x и Windows NT используется разная реализация таких функций.
Windows 9x допускает использование только ANSI-версий процедур и совсем не поддерживает Unicode. Но при этом она включает оба варианта функций, второй из которых на самом деле не работает. Представляется вполне логичным, если бы обращение, скажем к GetWindowTextW, вызывало бы сообщение об отсутствии такой функции. Однако в реальности данная функция не выполняет никаких действий и возвращает в вызывающую программу нулевое значение, которое можно интерпретировать как ошибку. Однако если программный код:
ItemHeight& = SendMessageA(.hWnd, LB_GETITEMHEIGHT, 0, 0) NewIndex& = ListHeight& \ ItemHeight&
работает нормально, то замена SendMessageA на SendMessageW вызовет программную ошибку « деление на ноль» .
В Windows NT оба варианта функций являются рабочими, что позволяет использовать кодировку и ANSI, и Unicode. В первом случае нужно обращаться к A-процедуре, которая преобразовывает символьный код из ANSI в Unicode и передает управление на собственно обработку в W-процедуру. Во втором случае нужно обращаться напрямую в W-процедуру.
32-разрядные версии Visual Basic используют Unicode для внутреннего хранения строковых переменных. Однако при обращении к API- или DLL-функциям, описанным с помощью оператора Declare, производится автоматическое преобразование символьных данных в ANSI (а потом — обратно). Таким образом, при работе с VB нужно обращаться к ANSI-варианту API-функций как в Windows 9x, так и Windows NT.
В принципе имя, типа SendMessageA, можно использовать непосредственно в программе. Но общепринятым считается применение имени функции без суффикса, для чего применяется параметр Alias:
Declare Function SendMessage ... Alias " SendMessageA"
Во-первых, это обеспечивает определенную совместимость имен с API Win16, а во-вторых, подчеркивает, что используется один из вариантов функции SendMessage.
КомпьютерПресс 6'1999