Power Basic DLL Compiler - инструмент, с помощью которого можно повысить производительность программ на Visual Basic. Особенности его применения рассмотрены в предлагаемой статье.

Еще до того как Power Basic DLL Compiler (PB/DLL) стал создавать самостоятельно выполняющиеся программы, он применялся в качестве инструмента для облегчения работы с Visual Basic (VB). Основная функция PB/DLL поставлять библиотеки DLL разработчикам программ на VB.

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

Созданные PB/DLL стандартные DLL-файлы могут быть корректно вызваны практически из любого языка программирования, поддерживающего динамически подключаемые библиотеки. Данные могут возвращаться из функций напрямую посредством 16- или 32-разрядных регистров, либо через 32-разрядный указатель (при работе с числами с плавающей точкой на машинах с математическим сопроцессором - через стек или скрытый стеко-ориентированный указатель). PB/DLL работает в среде Windows 3.xx, 95, NT и OS/2.

История этого компилятора началась с его аналога для DOS, который и по сей день остается самым совершенным и функциональным компилятором языка Бейсик и на 99% совместимым с Microsoft Quick Basic. Поэтому перевод программ, написанных на Бейсик для DOS, в Windows уже не составляет особого труда. Простота миграции между этими системами является основным достоинством PB/DLL. Конечно, исходный текст на VB отличается от написанного на DOS-версиях Бейсик поскольку изменились способы работы, например с экраном, принтером и последовательными портами. Решением этой проблемы и стало использование промежуточных DLL.

Таким образом, программирующие на VB смогли ускорить работу своих программ, а пользователи его 32-разрядных версий - применять Direct/32 для получения доступа к DLL, созданным c помощью PB/DLL.

Создать DLL несложно, нужно лишь записать одну функцию или более в текстовый файл, добавить несколько ключевых слов и откомпилировать. Поскольку синтаксис у PB/DLL почти такой же, как у VB, то обычно приходится изменять очень небольшой объем текста.

Программисты, работающие на Cи/Cи++ или Delphi, могут обращаться к PB/DLL, чтобы задействовать некоторые преимущества Бейсик- программирования и избавиться от проблем с типографическими ошибками в переменных, а 32-разрядные приложения могут получить доступ к портам и прямой доступ к памяти с помощью Direct/32.

Организовать вызов 16-разрядной DLL из 32-разрядного кода ранее было нелегко, так как требовались специальные thunk-скрипты и компиляторы, включая Cи-компилятор для создания единой одно-целевой DLL. И все это было нужно лишь для того, чтобы получить доступ к одной 16-разрядной функции. Для упрощения работы была разработана технология Power Basic Direct/32, позволяющая 32-разрядному коду напрямую вызывать 16-разрядные функции.

Для чего же может потребоваться вызывать 16-разрядную DLL из 32-разрядного приложения? В частности, для того чтобы она стала доступной из обеих платформ. Кроме того, вам могут понадобиться возможности, невыполнимые 32-разрядным кодом, подобные непосредственной работе с оборудованием или разделению доступа к основной памяти, которые могут быть реализованы в 16-разрядных программах. К сожалению, технология Direct/32 применима только в приложениях, запускаемых под Windows 95, и не функционирует в Windows NT, поскольку ее адресное пространство разделяется между 16- и 32-разрядными процессами.

С помощью PB/DLL можно ускорить написанные с помощью VB программы 5-15 раз. Для этого надо просто определить их узкие места и откомпилировать эти процедуры как DLL.

Взаимодействие Visual Basic и PB DLL Compiler

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

В качестве примера используем программу, создающую текстовый блок и две кнопки, при нажатии которых вызываются соответствующие подпрограммы. Первая кнопка, имеющая имя MaxInt, вызывает функцию, сравнивающую два целых числа и возвращающую большее. Вторая кнопка, названная CapFirst, вызывает подпрограмму, переводящую слова в верхний регистр (Листинг 1).

Листинг 1

Function MaxInt(ByVal Int1%, ByVal Int2%) As Integer
    If Int1% > Int2% Then
      MaxInt = Int1%
    Else
      MaxInt = Int2%
    End If
  End Function

  Sub CapFirst(zStr As String)
    Dim Curr As Integer
    Dim Prev As Integer
    Dim X As Integer
    Dim J As Integer

    Curr = 0
    Prev = 0
    J    = LEN(zStr)

    For X = 1 To J
      Curr = ASC(MID$(zStr(X)))              ' получаем текущий символ
      IF Prev = 32 Then                      ' если Prev = SPACE

        IF (Curr > 96) AND (Curr < 123) Then ' если Curr в нижнем регистре
          MID$(zStr(X),1) = CHR$(Curr-32)    ' преобразовать в верхний
        End If
      End If
      Prev = Curr
    Next
  End Sub

Для компиляции функции MaxInt в PB/DLL сначала сохраним выбранную форму VB, содержащую ее, как текстовый файл FORM1.TXT. Затем скопируем эту функцию MaxInt из файла FORM1.TXT, используя текстовый редактор, и запишем в новый файл MYFIRST.BAS, который будет содержать только ее. Прежде чем преобразовать этот файл в DLL, нужно осуществить только две операции. Во-первых, следует добавить метакоманду $COMPILE, которая сообщит компилятору, что требуется сделать DLL, а не отдельный EXE-файл. Она должна появиться в отдельной строке файла до первой выполняемой команды (до нее могут помещаться лишь комментарии) Во-вторых, в строке определения функции нужно написать ключевое слово EXPORT. Преобразованный модуль представлен на листинге 2.

Листинг 2

$COMPILE DLL                        ' необходимо для компиляции в dll

  Function MaxInt(ByVal Int1%, ByVal Int2%) EXPORT As Integer
    If Int1% > Int2% Then
      MaxInt = Int1%
    Else
      MaxInt = Int2%
    End If
  End Function

Ключевое слово EXPORT, включенное перед определением типа возвращаемого функцией значения, сообщает PB/DLL, что надо сгенерировать соответствующий код так, чтобы сделать ее видимой вне DLL. Если не использовать это слово, то функция будет работать, но она превратится в PRIVATE (частную), видимую только в DLL.

Нажав кнопку Build, можно скомпилировать файл, используя программу-оболочку PBSHELL. В результате на диске появится файл MYFIRST.DLL с экспортируемой функцией MaxInt. Для того чтобы вызвать ее из программы на VB, нужно добавить команду declare для этой функции в разделе описаний формы.

Declare Function MaxInt% Lib "MYFIRST.DLL" (ByVal Int1%, ByVal Int2%)

Эта строка сообщает VB, что будет вызываться функция, названная MaxInt, которая находится в библиотеке MYFIRST.DLL. Если ее не окажется в каталоге с программой VB, то нужно сообщить путь к ней.

Теперь взглянем на подпрограмму CapFirst, сохранив предварительно форму с ней в файле MYFIRST.BAS. Перемещение такой подпрограммы из VB в DLL не более сложно, чем перемещение ранее рассмотренной функции. Единственное различие заключается в передаче аргумента - программа действует со строками, а не с целыми числами. Когда в VB из подпрограммы, расположенной в DLL, через аргументы передается строка, заканчивающаяся нулем (ASCIIZ-строка), то ее обработка ведется именно до этого символа. Поэтому при перемещении CapFirst из VB в PB/DLL в первую очередь нужно изменить объявление аргумента со string на ASCIIZ. Это показывает компилятору, что zStr - это строка, заканчивающаяся нулем. Затем, как и в предыдущем примере, нужно дополнить определение подпрограммы ключевым словом EXPORT.

Sub CapFirst(zStr As ASCIIZ) EXPORT
....
End Sub

Как и ранее, это слово делает подпрограмму видимой и вызываемой извне DLL. Теперь можно скомпилировать текст MYFIRST.BAS в DLL, содержащую функцию MaxInt и подпрограмму CapFirst.

Форме VB нужно сообщить, где находить CapFirst Sub. Не забудьте закомментировать или удалить из нее исходный код последней. Описание для CapFirst будет выглядеть так:

Declare Sub CapFirst Lib "MYFIRST.DLL" (X$)

Следует отметить особенность преобразования констант при перенесении программ из VB в PB/DLL, где целые постоянные определяются непосредственно записью имени со знаком "%" (%MyConst = 42).

Тем не менее это не означает, что нельзя иметь любые другие постоянные величины в программе на PB/DLL. Вы можете имитировать числа с плавающей запятой и использовать константы, объявляя переменную желаемого типа и ее правопреемника. Наилучший путь для достижения такой цели - определить все константы как глобальные переменные и проинициализировать их в функции LibMain (в случае DLL), или WinMain (в случае EXE).

Скажем, в VB есть две константы:

Global CONST Name = " TEST " 
Global CONST Number = 1.2

В PB/DLL необходимо изменить объявления:

Global Name as string
Global Number  as single

Затем в LibMain или WinMain их нужно проинициализировать:

Name = " TEST "
Number = 1.2

После определения константа не должна изменять свою величину во время работы программы. Нужно гарантировать, что программа случайно не сделает этого с величиной, присвоенной одной из псевдо-констант.

Прямое обращение к памяти в DOS-сеансе Windows

Применяя PB/DLL можно, как и в старом добром Бейсике для DOS, напрямую обращаться к памяти, обходя запреты, наложенные в VB, поскольку компилятор поддерживает операторы PEEK и POKE, хотя и с некоторыми отличиями. В DOS сначала нужно с помощью оператора DEF SEG определить сегмент памяти, к которому хотелось бы иметь доступ, а затем использовать PEEK и POKE для указания смещения. Windows сохраняет структуру памяти, сегментированную по 64 Кбайт, знакомую большинству программистов по работе с DOS. Однако в ней вместо сегментов и смещений применяются селекторы и смещения. Первый является 16-разрядным индексом, хранящимся в таблице 32-разрядных адресов памяти. Когда в один из регистров сегмента записывается некоторое значение, происходит ошибка. Менеджер памяти Windows перехватывает ее и сравнивает величину в регистре со своим списком правильных селекторов. Если ее нет в спискеона не окажется правильным селектором, то выдается GPF (General Protection Fault - общая ошибка защиты).

Для того чтобы иметь доступ к адресу памяти, нужно записать правильный 16-разрядный селектор в один из сегментных регистров. Если поместить величину сегмента DOS в регистр, то возникает GPF, поскольку она не является селектором.

Так как же получить доступ к сегментам DOS из приложения Windows? Совсем просто: нужно запросить Windows о правильном селекторе для области памяти DOS, к которой хотелось бы иметь его. Сама система имеет список селекторных величин для основных сегментов памяти DOS, а PB/DLL предусмотрел заранее определенные переменные для каждого из этих селекторов:

pbv0000H  =  0000:0000
pbv0040H  =  0040:0000
pbvA000H  =  A000:0000
pbvB000H  =  B000:0000
pbvB800H  =  B800:0000
pbvC000H  =  C000:0000
pbvD000H  =  D000:0000
pbvE000H  =  E000:0000
pbvF000H  =  F000:0000

Для доступа к памяти PEEK и POKE в PB/DLL требуют 32-разрядные указатели, которые являются просто 16-разрядными селекторами, объединяемыми с 16-разрядными смещениями в 32-разрядные адреса, которые указывают на данную позицию памяти. Так, чтобы получить доступ к адресу ввода-вывода первого параллельного порта, находящегося в памяти по адресу 0040:0008, нужно объединить селектор 0040h и смещение восемь в 32-разрядный указатель:

Selector = pbv0040H
Offset   = 8
Pointer  = Selector * 65536 + Offset

Поскольку адрес порта ввода-вывода имеет длину два байта, следует пересчитать его с помощью функции PEEK.

Lpt1Address = PEEK(Pointer+1) * 256 + PEEK(Pointer)

Нелишне напомнить, что в данном случае, как и в обычной DOS, один селектор может адресовать до 64 Кбайт памяти. Чтобы получить доступ к следующим 64 Кбайт, нужно использовать новый селектор.

* * *

В заключение ответ на наиболее задаваемый вопрос: "Можно ли программу, написанную на VB, перекомпилировать на PB/DLL?"

PB/DLL - это не замена VB, а дополнительный инструмент. Он не полностью совместим с ним, не поддерживает формы и некоторые VB-объекты, например управление данными (для доступа в базы данных), но позволяет преобразовывать медленные части программы в DLL-библиотеки, вызывая которые можно значительно повысить производительность. А ради этого иногда стоит и помучиться.


ОБ АВТОРЕ:

Сергей Чабунин - технический координатор сети The Basic Network, занимающейся поддержкой разработчиков ПО.

E-mail: s_chabunin@yahoo.com

FidoNet: 2:5020/1281.10

Http://chabunin.hypermart.net

Http://www.basicnet.sonnet.ru


Коротко о продукте

Power Basic DLL Compiler - компилятор для преобразования фрагментов программ, написанных на Visual Basic, в DLL-библиотеки.

Операционные системы Windows 3.11, 95, NT и OS/2

PowerBASIC, тел.: (800) 780-7707,

http://www.powerbasic.com