PowerBASIC: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
→‎Ссылки: дополнение
Строка 600: Строка 600:
== Ссылки ==
== Ссылки ==
* [http://basicproduction.nm.ru/POWERBASIC/PowerBasicRTF.rar Оригинальная версия статьи]
* [http://basicproduction.nm.ru/POWERBASIC/PowerBasicRTF.rar Оригинальная версия статьи]

[[Категория:Языки программирования]]

Версия от 15:53, 30 апреля 2012

Внимание! Большое количество информации, написанной в данной статье, устарело.

Первоначально Бейсик предполагалось использовать в обучении, но в последствии его место в этой нише занял язык Паскаль, позволяющий расширять знания обучаемого программированию1. Рассматриваемый здесь PowerBasic (PB) по своей концепции2 не является полноценной заменой VB. В зависимости от ситуации, PB можно использовать как самостоятельный инструмент, так и в качестве дополнения к VB, но в некоторых случаях без него можно и обойтись. Для нас главная цель в изучении PB — восполнить атрофированную в VB6 обучательную функцию языка Бейсик.

Повышение уровня квалификации традиционно предполагает переход программиста с языка Бейсик на другой, более функциональный и менее ограниченный язык. Такой переход влечёт за собой известные трудности в переобучении и отказ от предыдущего опыта. PB предоставляет альтернативный, более лёгкий и менее трудоёмкий путь. Вам не нужно учить Си или Паскаль, чтобы преодолеть ограничения PowerBasic — их просто нет. Затраты на обучение минимальны по сравнению с традиционным способом и позволяют преодолеть барьер нехватки свободного времени или имеющихся знаний.

История PowerBasic

PB имеет давнюю традицию, которая уходит корнями в MS-DOS (Microsoft Disk Operation System - Дисковая операционная система, предшественница Windows) и тесно связана с Turbo Basic, являющимся одним из лучших компиляторов языка Бейсик для ДОС (об этом свидетельствует распространённость Turbo Basic в учебных заведениях бывшего СССР, а затем и стран СНГ)3. Turbo Basic — фактически и есть PowerBasic, только доведённый до уровня продукта компанией Borland (сейчас Inprise)4.

Пик расцвета языка Бейсик приходится на средину-конец 80-х годов XX столетия. В те годы он был развит настолько, что считался профессиональным инструментом, был совместим со всеми другими языками и входил в междуязыковое программистское сообщество. Последующая затем монополизация корпорацией Майкрософт права на производство языка Бейсик вывела его из междуязыкового сообщества программистов и ограничила его узкими рамками, исключающими какую либо совместимость с другими языками на добрый десяток лет, вплоть до формирования нового, подконтрольного Майкрософт, сообщества .NET программистов5. Возможно, действия Майкрософт оправдываются заботой о чистоте языка Бейсик, но демократичными их назвать трудно. Майкрософт заключила специальный договор с компанией Borland, по которому последняя отказалась от дальнейшей разработки компиляторов языка Бейсик и вернула права на Turbo Basic его создателю — Бобу Зейлу (Bob Zale), который продолжил его развитие под маркой PowerBasic, однако при всех своих талантах программиста серьёзно конкурировать с крупнейшей мировой корпорацией не смог.

Компилятор PowerBasic.

В PowerBasic, как и в VB, статическое связывание с библиотеками отсутствует. В этом PB похож на VB и отличается (к сожалению в худшую сторону) от остальных языков.

Однако, PB является полноценным компилятором, интерпретирующая часть в нём ничтожна. Создаваемые компилятором приложения являются полностью 32-битными и поддерживает только 32-битные платформы(Windows’9x/NT).

Компилятор PB является однопроходным оптимизирующим. Что это такое? Прежде всего определимся как устроен компилятор. Как известно, процесс компиляции разбивается на несколько этапов: лексический, синтаксический (грамматический) и семантический (смысловой) анализ, генерация промежуточного и объектного (машинного) кода.

Лексический анализатор (ЛА) преобразует поток байт (текст исходного кода) в минимально допустимые элементы языка: ключевые слова (терминалы), имена переменных и процедур (идентификаторы), строчные и числовые константы (литералы). Результатом работы ЛА является поток этих элементов (лексем).

Синтаксический анализатор проверяет соответствие вложенной в него грамматики с грамматикой исходного кода, при этом разбивая выражения (например, A=1+2*(3-4)) на простейшие операции.

Завершает анализ Семантический анализатор, который производит смысловой анализ. Например, была ли объявлена переменная A и совпадают ли типы операндов в каждой операции (А=1; Здесь и "А" и "1" должны иметь числовой тип). Промежуточный код представляет собой поток этих простейших инструкций, которые могут быть транслированы в инструкции конкретного процессора.

В однопроходном компиляторе все стадии анализа выполняются в одном цикле, что гораздо ускоряет скорость компиляции.

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

%TWO = 2 'Константа
a = 1 + %TWO

может быть записано минуя тупиковые операции

a = 3

Или например в условии

If a(y*3) < 0 OR b(y*3) > 10 Then a(y*3) = 0

три умножения y*3 могут быть вынесены за пределы выражения и принять вид

с=y*3
If a(с) < 0 OR b(с) > 10 Then a(с) = 0

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

Стоит отметить, что оптимизация компилятора PB уступает оптимизации компиляторов от Майкрософт. Так, например, у компилятора PB сильно хромает оптимизация на уровне процедуры, что приводит к бесполезному избыточному коду в начале тела любой процедуры или функции. Кроме того, если компилятор Майкрософт Си++ имеет два режима оптимизации: по размеру или по скорости, то PB оптимизирует только по размеру. Скорость чистого PB кода (без использования встроенного ассемблера) равна скорости кода, генерируемого компилятором Си в режиме оптимизации по размеру. Значит ли это что максимальная скорость компиляторов Си недостижима для компиляторов PB? Отчасти это так, крупнейший компьютерный гигант Корпорация Майкрософт приложила немало усилий для улучшения оптимизации именно Си компилятора. Слабый Си программист напишет программу, которая за счёт компилятора будет работать быстрее программы, написанной слабым программистом на PB. Однако по мере роста профессионального уровня программиста эти различия постепенно сглаживаются.

Итак, давайте посмотрим какие средства предоставляет язык PowerBasic. Во первых, PB создаёт компактные автономные приложения, не требующие внешних библиотек для запуска11. Во вторых, программа на PB запускается значительно быстрее чем программа на VB. Промежуток между началом загрузки программы в память и началом её работы практически не заметен.

PowerBasic имеет три модели приложений, реализованные двумя компиляторами:

  1. PB/WIN может создавать оконные приложения или библиотеки динамической связи;
  2. PB/CC (Console Compiler) создаёт приложения с текстовым интерфейсом.

Те, кто не знаком с консольным интерфейсом могут по ошибке принять его за приложение MS DOS. На самом деле это полноценное 32-разрядное приложение, реализующее, предоставленную ОС Windows возможность создавать приложения с оконным либо текстовым интерфейсом.

Язык в обоих компиляторах полностью идентичен, исключение составляют команды создания интерфейса между приложением и пользователем. В консольно-ориентированном компиляторе средства создания интерфейса довольно просты и идентичны ДОС-овским Бейсикам. Программист, привыкший к QB или TB может практически не переучиваясь перейти с 16-разрядного на 32-разрядный консольный компилятор. Оконно-ориентированный компилятор содержит средства для создания оконных приложений, включая дизайнер форм.

Оба компилятора поддерживают любые средства, предоставляемые операционной системой Windows, но в каждом присутствуют дополнительные средства, ориентированные на облегчение работы со своим интерфейсом (графическим или консольным).

Базовые элементы языка.

PowerBasic не опирается на какую либо из технологий (например, VB6 опирается на технологию COM), а придерживается другой концепции, которая заключается в упрощённом представлении технологий, входящих в состав операционной системы. Некоторые средства PB могут показаться сложными или неудобными, однако все они отражают процессы, реально происходящие в операционной системе. Некоторые средства упрощены по сравнению с аналогичными средствами ОС, но, как правило, это не искажает картины в целом.

Синтаксически PowerBasic практически полностью совместим с VB. Два важных отличия заключаются в способе представления констант и имён подпрограмм обработки строк.

Константы определяются только в секции глобальных объявлений (см. 5) и по записи отличаются от других элементов языка приставкой слева к имени константы специального знака. Например, $Строка — "строковая константа", а %Число — соответственно числовая. При конвертировании приложения с VB на PB константы можно эмулировать с помощью макрокоманд (см. 11.1).

Все подпрограммы, обрабатывающие строки (например, MID$), должны иметь знак доллара в конце. Такая запись верна и в VB, но в нём строковые подпрограммы по умолчанию ориентированы на тип Variant и указание знака доллара в VB переориентирует подпрограммы на строковый тип. Здесь, к сожалению, универсального решения нет. При переписывании кода с VB на PB вам придется добавлять знаки $ вручную.

И последняя, важная на мой взгляд особенность, отсутствие автоматического преобразования типов, которое присуще VB. Так, команда вывода сообщения требует в качестве сообщения строку. Всего мы рассмотрим 4 базовых элемента, отсутствующих в VB.

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

Указатель (Pointer)

Чтобы понять что такое указатель нужно сначала взглянуть на устройство переменных. При компиляции исходного текста программы в исполняемый файл имена переменных заменяются адресами памяти. Когда вы присваиваете переменной какое либо значение происходит запись этого значения по адресу переменной. Адреса эти высчитываются компилятором и хранятся внутри создаваемого приложения. Иными словами, адрес обычной переменной является неизменным — это константа. В отличие от обычной переменной, указатель имеет плавающий адрес, который может быть изменён в любой момент. Например, создав обычную переменную типа "строка" и переменную-указатель типа "байт", можно обращаться к символам внутри строки, предварительно присвоив указателю адрес начала (или любого символа) строки. Обратившись к указателю по его имени можно задать или получить текущий адрес данных, на которые ссылается указатель. Для того чтобы обратится к самим данным нужно подставить к указателю в качестве приставки знак @. Если указатель ссылается на массив (или просто вам удобно представлять какие-нибудь данные через массив), то необходимо в конец указателя добавить в квадратных скобках индекс элемента массива. Например, @Указатель[1].

Объединение (Union)

Следующее расширение, называемое Объединением, по способу использования похоже на пользовательские типы (type…end type), однако по внутреннему представлению они кардинально различны. В отличие от пользовательского типа, в котором каждый элемент это отдельное значение, в объединении есть всего одно значение, которое интерпретируется в зависимости от того, какой элемент объединения используется в данный момент. Общий размер объединения равен длине самого большого элемента. К примеру, тип Variant в VB является объединением, что позволяет содержать внутри него данные любого типа (если точнее, Variant является пользовательским типом, состоящим из двух переменных типа Long и одного 8-байтного объединения).

Строка фиксированной длины с нулём в конце (Asciiz)

Строки фиксированной длины с нулём в конце сильно отличаются от обычных динамических строк. Динамические строки в PB соотетствуют типу BSTR технологии COM, но используют для хранения одного символа один байт (кодировка ANSI). Динамические строки, по сути, являются объектами: когда вы изменяете размер строки, на самом деле происходит вызов функций из системной библиотеки OLE32.DLL, которая создаёт новую строку нужного размера и копирует туда данные из объединяемых строк. Динамическая строка, подобно указателю, не имеет постоянного места жительства и является переменной с плавающим адресом. Строки фиксированной длины с нулём в конце подобны обычным переменным и имеют фиксированный адрес. Из за фиксированного размера объединение данных в таких строках происходит быстрее чем в динамических (не нужно создавать новые строки и копировать в них данные), но размер их, единожды заданный, нельзя изменять на протяжении всей работы программы.

Беззнаковые числа (Word, Dword, Qword)

Чтобы понять работу беззнаковых чисел нужно разобраться в устройстве знаковых. Например, знаковое Integer и беззнаковое Word имеют размер 2 байта(16 бит), соответственно могут иметь значения в диапазоне от 0 до (2^16)-1. Беззнаковые числа имеют только положительные значения, их диапазон от 0 до 65535, в знаковых же половина диапазона используется для положительных и половина для отрицательных значений (от −32768 до 32767).

Без знака   Со знаком
32767       32767
32768       -32768
32769       -32767
...         ...
65534       -2
65535       -1

Теперь можно приступить к описанию остальных частей языка.

Минимальное приложение.

Как и в VB, минимально допустимая конструкция в PB состоит из стартовой процедуры, только в PB она является функцией и называется PbMain. Кроме того, необходимо специальной директивой #Compile указать компилятору тип создаваемого файла.

#Compile Exe
Function PbMain
 MsgBox "Минимальное приложение.", 64
End Function

Если рассмотреть минимальное приложение поблочно, то можно разделить его на секцию глобальных объявлений (начало первой процедуры символизирует её окончание) и секцию процедур. Как и в VB, внутри процедур PB нет жёсткого разделения на секции объявлений и кода (можно объявлять переменные в любом месте программы).

Динамическая библиотека.

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

#Compile Dll
Function PbLibMain
 [...]
End Function

Стартовая процедура библиотеки отличается от стартовой процедуры приложения тем, что в приложении по завершении работы кода функции PbMain (или принудительного выхода из неё) полностью завершается работа программы, а функция PbLibMain служит для инициализации при подключении библиотеки к процессу или потоку. Если вы хотите управлять инициализацией, то вы должны сменить PbLibMain на LibMain, предоставляющую более широкие возможности. Стартовая процедура в библиотеке является служебной, а исполняемый код библиотеки должен находиться внутри экспортируемых функций. Строение библиотеки здесь можно сравнить с объектом. Экспортируемые функции являются публичными и могут быть вызваны из приложения, а неэкспортируемые являются приватными и видны только внутри библиотеки. Экспортируемая процедура или функция имеет два дополнительных ключевых слова в описании каркаса.

Function MyFunc Alias "MyFunc"(Parameter As Long) Export As Long
End Function

Здесь Alias "MyFunc" определяет регистрозависимое имя, под которым процедура будет доступна из вне, а слово Export говорит о том что она внешняя. Библиотеки, создаваемые PB, являются обычными не ActiveX библиотеками, их не нужно регистрировать в ОС, но функции, которыми вы будете пользоваться, должны быть объявлены в вызывающем модуле. Например, объявление в VB:

Declare Function MyFunc Lib "pb.dll" Alias "MyFunc"(Parameter As Long) As Long

Если имя и псевдоним функции одинаковы, VB уберёт Alias"MyFunc", считая их равнозначными. В любом случае псевдоним (Alias"MyFunc") должен быть идентичен псевдониму в PB (именно псевдоним записывается внутрь библиотеки как имя экспортируемой функции). Пример вызова процедуры из библиотеки, написанной на PB. Примечание, во избежание сообщения "Невозможно найти библиотеку" в VB, запускайте проект из папки, в которой находится внешняя библиотека. Код для VB:

Private Declare Sub TestSub Lib "testdll.dll" (ByVal StrTest As String)
Private Sub Form_Load()
 Dim Str As String
 Str = "Hello World!"
 TestSub Str
 MsgBox Str, 64
End Sub

Код для PB:

#Compile Dll "testdll.dll"
Function PBLibMain
 Function = 1
End Function
Sub TestSub Alias "TestSub" (StrTest As Asciiz) Export
 StrTest="Привет Мир!"
End Sub

VB при передаче во внешнюю библиотеку строки по значению (ByVal), создаёт копию этой строки в формате Asciiz и передаёт в библиотеку указатель на эту копию. После того как внешняя процедура отработала, копия строки преобразуется обратно. Подстраиваясь под эту особенность, процедура в библиотеке может принять копию строки как "строку фиксированной длины с нулём в конце". Соответственно изменить её размер она не может, зато может изменить её данные.

ООП.

PowerBasic не имеет встроенных средств для работы с объектами, однако содержит инструменты, облегчающие работу с технологией COM (составная объектная модель), которая является реализацией ООП от Майкрософт. Возможность создания COM-клиента позволяет устанавливать интерфейс с COM-серверами.

Сервером COM является постоянно находящийся в памяти программный модуль, ожидающий вызовов от COM клиентов. Для того чтобы обратиться к COM серверу необходимо знать его уникальный номер, записанный в реестре. Этот номер может быть как представлен в виде 16-байтовой записи, содержащей уникальную последовательность цифр, так и выражен через запоминающееся символическое имя. Первый вид представления называется идентификатором класса(CLSID), а второй идентификатором программы(PROGID). Идентификатор программы наиболее удобный для запоминания, например COM сервер приложения Word 2002 из пакета MS Office имеет идентификатор программы "Word.Application.10". Мост между сервером и клиентом создаётся с помощью специальной объектной переменной. Технология COM использует два вида связи клиента с сервером — раннюю и позднюю. Ранняя связь подразумевает описание состава объекта программистом, а в позднем связывании состав объекта задаётся COM сервером автоматически. Соответственно существует два типа объектов, предназначенных для каждого типа связывания клиента и сервера COM. Благодаря своей простоте, второй тип связывания (позднее связывание) более удобен для программиста. Для работы со значениями методов и свойств объектов в PB существует переменная изменяемого типа(Variant), идентичная переменной с таким же типом в VB6. Работа со свойствами объекта осуществляется командами LET и GET, методы вызываются командой CALL, а ссылки между объектами устанавливаются командой SET.

Давайте рассмотрим пример, иллюстрирующий насколько проста работа с объектами в ПБ.

'Первая переменная это объект, а вторая изменяемого типа
Dim oWord As Dispatch, vBool As Variant
'Word.Application.10 - это PROGID, определяющий имя сервера
'Dispatch - это объект позднего связывания
'попытка связи клиента с сервером
Let oWord = Dispatch In "Word.Application.10"
'Если Word уже был запущен, соответственно установится связь,
'в противном случае объектная переменная останется пустой.
'Если объект пустой значит нужно запустить сервер (ключевым словом New)
If IsFalse IsObject(oWord) Then Let oWord = New Dispatch In "Word.Application.10"
'Значения, передваемые (получаемые) в объект, 
'должны храниться в переменной изменяемого типа
Let vBool = 1
'Изменяется одно из свойств объекта командой Let
'В данном случае устанавливается видимость.
Object Let oWord.Visible = vBool

Интерфейс позднего связывания (Dispatch) предоставляет удобное средство взаимодействия с COM, однако оставляет нераскрытым вопрос где же брать описание объекта. Если при раннем связывании объект описывается программистом, то для позднего связывания существует специальный инструмент — обозреватель объектов (PowerBasic COM Browser).

Многопоточность.

В отличие от VB6, PowerBasic поддерживает многопоточность как в приложениях, так и в библиотеках и предоставляет простые инструменты для её обслуживания. Многопоточность даёт возможность одновременного выполнения различных подпрограмм. Потоки, как и процессы, являются составной частью всех приложений в MS Windows. Когда вы запускаете программу, в любом случае создаётся, как минимум, один процесс, содержащий один поток (например, exe-файл, компилированный в VB6). Процессы — это программные объекты, позволяющие программам работать отдельно друг от друга путём предоставления каждому процессу персонального виртуального адресного пространства. Потенциально процессы заложены в микропроцессор и полностью реализуются средствами операционной системы. Виртуальное адресное пространство организовано за счёт того, что адреса памяти, записанные внутри программы, являются не настоящими, а так называемыми "линейными" адресами, которые посредством механизма страничной адресации, заложенного в процессор, преобразуются в физические. Механизм страничной адресации разбивает виртуальный адрес на три части и находит реальный адрес, пользуясь таблицами страниц памяти. Многопроцессность и многопоточность реализуются путём последовательного переключения между процессами и их атрибутами. В однопроцессорной ЭВМ многопроцессность и многопоточность фактически эмулируются, поэтому суммарное время выполнения множества потоков близко ко времени выполнения одного потока. Однако в разных потоках можно производить совершенно разнотипные действия. Простейшим примером является ситуация, когда программа, занятая расчётом, не реагирует на действия пользователя (проще говоря, подвисает). Создав для расчёта отдельный поток, можно в основном потоке начать настройку следующего этапа расчёта. Такой конвейерный способ взаимодействия пользователя с приложением позволяет добиться большей продуктивности.

Приведённая ниже модель позволяет создать только один поток для процедуры Thread1, что в принципе не отменяет создание новых потоков в других процедурах. Кроме того, такая модель не является аксиомой12(просто к такой реализации я пришёл методом научного тыка, возможно вы найдёте и лучше), можно создавать сколь угодно много отдельных потоков в одной и той же процедуре.

'Глобальная переменная для хранения состояния потока
'Объявляется в секции глобальных объявлений
Global hThread1 As Long
Sub RunNewThread()
'для хранения состояния потока
Dim Thread1Status As Long
'какое либо значение для передачи в поток
Dim Value As Long
'проверить состояние потока
Thread Status hThread1 To Thread1Status
'если поток уже запущен
If Thread1Status = &H103 Then
  Dim Thread_ExitCode As Long
  'получить пароль для выхода из потока (Win32 API)
  GetExitCodeThread hThread1, Thread_ExitCode
  'принудительное завершение потока (Win32 API)
  TerminateThread hThread1, Thread_ExitCode
  Dim Thc_Result As Long
  'уведомление о закрытии потока
  Thread Close Thrd_DlgReplace To Thc_Result
End If
'создать новый поток в процедуре Thread1
Thread Create Thread1(Value) To hThread1
End Sub
Function Thread1(ByVal wParam As Long) As Long
'данная процедура будет запущена в новом потоке
'и будет выполняться параллельно с другими потоками
 MsgBox Str$(wParam)
End Function


Графический интерфейс пользователя.

Базовым объектом графического интерфейса в MS Windows является окно. Окна отличаются друг от друга по именам классов. Например, поле ввода текста имеет класс "Edit", а класс окна со списком называется "ListBox". Чтобы подействовать каким либо образом на окно необходимо послать ему соответствующее действию сообщение. Каждому окну сопутствует оконная процедура, в которой программист описывает реакции на поступившие сообщения. Сообщения, не обработанные в оконной процедуре, пересылаются в оконную процедуру по умолчанию, где обрабатываются операционной системой. PowerBasic не имеет собственной среды для создания оконного интерфейса, существующие инструменты являются упрощённым переходником к функциям ОС. Однако следует учитывать что создание сколь нибудь сложной интерактивной среды невозможно без интерпретирующей части, влекущей за собой зависимость от внешних библиотек или разрастание размера исполнимого файла (лидеры на рынке языков программирования корпорации Майкрософт и Инпрайз(Борланд) тому наглядный пример). Ради сохранения принципов минимализма разработчики отказались от интерактивной среды в пользу малого размера выпускаемого компилятором приложения. Создание окон в PowerBasic осуществляется либо специальными оконными командами, либо визуально в дизайнере форм. Процессы создания окна и обработки событий(сообщений) в PowerBasic не объединены и осуществляются в два этапа. Компоновка должна быть заранее спланирована на бумаге или в дизайнере форм и только затем может быть перенесена в исходный код программы. Если не следовать поэтапному принципу, то позже будет достаточно сложно добавлять новые элементы или удалять лишние. Предоставляемые PowerBasic средства для работы с окнами позволяют создавать диалоговые формы и стандартные элементы управления. Итак, что представляет собой простейшая конструкция окна в PowerBasic? На диалоговой форме располагаются элементы управления. Диалоговая форма имеет логический номер (Handle of Window, рукоять или ручка окна), который является его уникальным номером, позволяющим отличить его среди других окон в операционной системе. Элементы управления (э.у.) пользуются ручкой диалоговой формы (которая является для них родительским окном) и различаются по своим порядковым номерам внутри этой формы. И если ручка диалогового окна является случайным числом, создаваемым операционной системой, то порядковые номера э.у. задаются программистом. Команда создания диалоговой формы довольно проста, в самом сокращённом варианте нужно указать лишь имя заголовка и размеры

 Dialog New 0, "Заголовок",,, 160, 50 To hDlg

Здесь цифра, следующая за ключевым словом New, указывает на родителя создаваемого окна. В данном случае 0 — Рабочий стол. Подряд идущие запятые значат что какие то значения берутся из значений по умолчанию (либо не используются вовсе). В данном случае это стартовые позиции окна. Далее следуют ширина и высота, а конечным параметром является приёмник для ручки окна. В данном случае ручка окна будет сохранена в переменную hDlg.

Более сложные окна требуют комбинации обычных и дополнительных стилей, которыми можно настраивать множество параметров создаваемых окон. Существуют как специализированные стили, характерные только для окон определённого класса, так и стили, общие для всех окон. Настраивать стили рекомендуется используя дизайнер форм, так как ручная настройка стилей довольно кропотливая работа. Но полагаться полностью на дизайнер форм всё же не следует. В любом случае вы должны разобраться с константами стилей. Общие стили для всех окон находятся в константах, начинающихся с приставки "%WS_" (Window Style), дополнительные стили начинаются с приставки "WS_EX_" (Window Extended Style). Приставки остальных констант описаны в документации соответствующих окон. Например, стили текстового поля имеют приставку "%ES_" (Edit Style), а константы стилей списка имеют приставку "LBS_" (ListBox Style).

Элемент управления добавляется командой с практически идентичным строением, за исключением того, что должна быть указана принадлежность его окна к определённому классу. Имена стандартных классов пишутся прямым текстом, а имена дополнительных классов берутся в кавычки.

Control Add TextBox, hDlg, %IDTEXT, "", 14,  12, 134, 12

Здесь TextBox указывает на то что э.у. принадлежит к классу текстового поля. То что имя класса не берётся в кавычки говорит о том, что это стандартный класс. hDlg — ручка родительской диалоговой формы. %IDTEXT — порядкой номер э.у.(произвольное число, задаётся программистом), может быть просто числом, но рекомендуется использовать константы. Остальные параметры задают положение э.у. на диалоговой форме, его размеры и текст.

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

#Compile Exe
'Системная константа
'Может объявляться либо непосредственно,
'либо путём включения заголовочного файла директивой #INCLUDE
%BN_CLICKED = 0
'Порядковый номер (идентификатор) элемента управления.
'Может быть просто числом, но удобней использовать константу.
%IDOK = 1
'Объявление процедуры, обрабатывающей событие кнопки %IDOK
'(только объявленная процедура может располагаться после вызывающей её).
Declare CallBack Function OkButton() As Long
Function PBMain
Dim hDlg As Long, Result As Long
'Создать диалоговое окно
Dialog New 0, "Press button.",,, 160, 50 To hDlg
'Создать э.у.
Control Add Button, hDlg, %IDOK, "OK", 34, 32, 40, 14 Call OkButton
'Показать диалоговое окно модально
Dialog Show Modal hDlg To Result
End Function
'Оконная процедура э.у.
CallBack Function OkButton () As Long
   Dim Caption As String
   If CbCtlMsg = %BN_CLICKED Then
       Control Get Text CbHndl, %IDOK To Caption
       MsgBox "Вы нажали <<" & Caption & ">>"
       Dialog End CbHndl, 1
       Function = 1
   End If
End Function

Для понимания механизма оконной процедуры необходимо разобрать её внутренние ключевые слова: CBHNDL — ручка окна CBMSG — номер сообщения, посланного окну. Предназначено для оконной процедуры диалоговой формы; CBCTL — порядковый номер э.у. на диалоговой форме. Предназначено для оконной процедуры э.у.; CBCTLMSG — уведомление родительского окна о событии в э.у. Предназначено для оконной процедуры э.у. Если э.у. не имеет собственной оконной процедуры, то в диалоговой форме уведомление от э.у. можно узнать проверкой номера поступившего в форму сообщения. Если CBMSG=%WM_COMMAND, значит в CBCTLMSG находится номер уведомления от э.у.; CBLPARAM — какой либо параметр; CBWPARAM — какой либо параметр. Как правило, для каждого сообщения существуют жёстко заданные параметры (CBLPARAM и CBWPARAM). Здесь PowerBasic не имеет каких либо средств упрощения и полностью полагается на операционную систему. Посмотреть типы параметров для конкретного сообщения можно в справочниках по Win32 API либо MSDN.

Inline Assembler.

Inline Assembler в PowerBasic, хоть и уступает встроенным ассемблерам продуктов корпорации Майкрософт, всё же является довольно мощным инструментом управления микропроцессором. Кроме обычных инструкций процессора поддерживаются операции с плавающей точкой и потоковые инструкции для целых чисел. Потоковые инструкции для чисел с плавающей точкой не поддерживаются. Интересным фактом является то, что патроном Inline Assembler в PB является Стив Хатчсон, известный тем что переделал пакет MASM от Microsoft так, что его стало возможным использовать для программирования в Windows также легко, как например Си. Microsoft сочла использование ассемблера нерентабельным для создания пользовательских приложений, а ограничения защищённого режима не позволяют использовать все возможности пакета MASM в ОС Windows. Давайте разберёмся почему. Аппаратно микропроцессор (МП) поддерживает 4 уровня доступа к ресурсам, которые называются привилегиями (или кольцами). Операционная система Windows использует только два из них: 0-полный доступ и 3-пользовательский. Ядро ОС и драйверы устройств работают при полном доступе, а приложения работают с правами пользователя. То есть любая запущенная в Windows программа имеет минимальный набор прав. Так, например, в любой версии Windows запрещены прерывания, а в Windows NT запрещена работа с портами. Доступа к оборудованию вы не имеете, всё что вы можете делать в режиме пользователя — это только считать (и вызывать подпрограммы из системных библиотек), с чем в последнее время компиляторы справляются вполне сносно (всё же стоит отметить что подпрограмма, написанная командами PowerBasic всё ещё сильно уступает в скорости аналогичной подпрограмме, написанной командами Inline Assembler).

Итак, что представляет из себя ассемблер в режиме пользователя? Во первых писать на Inline Assembler в PB настолько легко и просто, что даже я, не написавший ни одной программы для ДОС и работавший с прерываниями только в своём воображении, вполне могу описать алгоритм поиска подстроки в строке или кодирования BASE64.

Программисту доступны 32-х разрядные регистры общего назначения (РОН) и 32-х разрядные адреса памяти. Каждый запущенный процесс — это отдельный виртуальный компьютер с изолированным адресным пространством. Соответственно две разные программы могут иметь различные данные по одинаковым адресам. Прочесть данные из другого процесса можно только специальными системными вызовами. Сегментные регистры заняты операционной системой. При работе со стековыми регистрами в PB следует учитывать что EBP всегда является локальной базой стека в процедуре, а ESP его указателем. Каждая процедура имеет свой локальный стек. Это достигается путём сохранения указателя стека (ESP) в базе стека (EBP) при входе в процедуру и восстановлении при выходе. Следующий пример иллюстрирует простоту использования ассемблера в PowerBasic:

Function CalcASM(ByVal Value As Long) As Long
' Переслать константу в счётчик цикла
! MOV ECX,10
' Переслать параметр в аккумулятор
! MOV EAX,Value
Label1:
' В цикле к значению прибавляется 1
! INC EAX
' Уменьшить счётчик цикла на 1
! DEC ECX
' Продолжить цикл
! JNZ Label1
' Функция возвращает результат
! MOV Function,EAX
End Function

Другие расширения и дополнения языка (детально).

Макрокоманды.

Макрокоманды являются ещё одним расширением языка и имеются во всех развитых языках программирования, как в высокоуровневых (Си/Паскаль), так и в Ассемблере. Макрокоманда служит для представления часто встречающегося однотипного кода под одним именем. В отличие от функции, которая существует в одном экземпляре и просто вызывается когда это необходимо, содержимое макрокоманды подставляется вместо её имени всякий раз когда она встречается. Способом применения макрокоманда похожа на константу, где имя константы заменяется соответствующим ей значением при компиляции. И если константа представляет собой какое либо значение (число или текст в кавычках), то макрокоманда является константой для исходного кода. В простейшем случае макрокоманда выглядит так:

MACRO Имя [(Параметр1, Параметр2, ...)] = <Выражение>

Если параметры существуют, то логично их будет использовать в выражении. Здесь левая часть является описанием макрокоманды, по которому она находится компилятором, а правая часть — тело макрокоманды, которое будет подставлено в исходный код при компиляции. Макрокоманды позволяют расширить синтаксис языка новыми ключевыми словами. Например, определение констант в VB может быть выражено через:

MACRO CONST = MACRO
...
CONST Version  = 1&

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

MACRO Имя [(Параметр1, Параметр2, ...)]
 [MACROTEMP Идентификатор1 [,Идентификатор2, ...]]
 DIM Идентификатор1 AS Type [,Идентификатор2 AS Type, ...]]
 {тело макроблока}
 [EXIT MACRO]
 {тело макроблока}
END MACRO

MACROTEMP позволяет создавать идентификаторы и автоматически следит за их уникальностью. Так, например, если бы вы каждый раз кусок однотипного кода писали вручную (без использования макроблока), вам бы пришлось и самому придумывать имена переменных для каждого такого блока. Объявлять дважды переменную с одним именем нельзя, поэтому уникальность переменных, заявленных ключевым словом MACROTEMP осуществляется путём приставки к правой части переменной возрастающего при каждом обращении к макроблоку числа. Например, если в теле макроблока встречается переменная MyVariable, то при первой подстановке в код она будет носить имя MyVariable000, при второй подстановке MyVariable001 и т. д.

Макрофункция является комбинацией макроблока и линейной макрокоманды. Соответственно тело макрофункции является их гибридом. Для объявления макрофункции в начало макроблока добавляется ключевое слово FUNCTION, а в конце конструкции указывается возвращаемое значение:

MACRO FUNCTION Имя [(Параметр1, Параметр2, ...)]
END MACRO = Значение

Обработка массивов.

ARRAY {ASSIGN | DELETE | INSERT | SCAN | SORT}

Предоставляет автоматические средства для обработки массивов. ASSIGN — аналог функции Array в VB6. В отличие от VB6 применителен к массиву любого типа (в VB6 функция Array позволяет работать только с типом Variant). Требует 8-ю версию оконного компилятора или 4-ю консольного. DELETE — Удаление элемента из массива. По умолчанию удаляет первый элемент и сдвигает все нижестоящие элементы на одну позицию вверх. Размер массива (общее количество элементов) автоматически не изменяется. Дополнительными ключевыми словами команды DELETE можно задать индекс удаляемого элемента (Массив([Индекс])) и количество сдвигаемых вверх элементов (For "Количество"). Так как размер массива не изменяется, то остаётся пустой элемент, значение которого также можно задать в команде. INSERT — Добавление элемента в массив. Вставляет элемент в массив и сдвигает все последующие элементы на одну позицию вниз. Устроен аналогично команде ARRAY DELETE. SCAN — ищет в массиве заданное выражение. По умолчанию выражение ищется начиная с первого элемента массива и до конца массива. Стартовый элемент поиска (Массив([Индекс])) и количество перебираемых элементов (For "Количество") может быть настроено. Для поиска в массиве строк дополнительно есть ключевые слова, определяющие регистронезависимость (строчные символы равны заглавным) символов и стартовую позицию внутри элемента (с какого символа строки начинать поиск). Регистронезависимость поддерживается только для латинских символов, но возможна настройка для других кодировок. Для этого создаётся строка из 256 байт, содержащая все 256 кодов символов таблицы ASCII и на месте кодов строчных букв проставляются коды заглавных (и наоборот).

К примеру, символ номер 194 содержит код 193 (буква "Б"), а символ номер 226 содержит код 225 (буква "б"). Заменив значение символа под номером 226 (смещение на одну позицию происходит потому что первым символом является 0, который тоже входит в таблицу ASCII) мы получим два одинаковых кода по разным позициям (в нашем случае 225 заменяется на 193).

'Объявить динамический массив и задать его размер
Dim A$(): ReDim A$(1 To 10)
'Присвоить 7-мому элементу строчный литерал
A$(7)="Привет мир"
'Создать строку с таблицей символов
C$ = Chr$(0 To 255)
'Заменить коды строчных символов на коды заглавных
Dim PosU As Integer
For PosU=1 To Asc("Я")-Asc("А")+1
    Mid$(C$,Asc("а")+PosU,1)=Mid$(C$,Asc("А")+PosU,1)
Next PosU
'Команда поиска, здесь:
'From 8 To 11 - искать с символа номер 8 по 11-й в каждом элементе массива;
'Collate C$ - использовать пользовательскую таблицу;
'= "МИР" - Искомое выражение;
'To I& - результат.
Array Scan A$(), From 8 To 11, Collate C$, = "МИР", To I&
MsgBox Str$(I&)

SORT — упорядочивание массива. По умолчанию упорядочивается по возрастанию (ASCEND), начиная с первого элемента. Порядок сортировки (ASCEND или DESCEND) и стартовый элемент (Массив([Индекс])) могут быть настроены. Построение команды аналогично ARRAY SCAN, но имеется дополнительное ключевое слово TAGARRAY. Этим словом указывается второй массив, зависимый от сортируемого массива. Перестановка элементов в сортируемом массиве повлечёт за собой перестановку элементов и в зависимом массиве. Элементы в зависимом массиве будут выстроены в порядке сортируемого массива.

Матрицы.

MAT

Производит алгебраические операции над матрицами, выраженными через массив. Операции состоят из арифметических (сложение, вычитание, умножение) или одной из: CON[(expr)], IDN, ZER, INV, TRN. CON — заполняет матрицу единицами; CON(expr) — присваивает каждому элементу матрицы результат выражения в скобках; IDN — создаёт в матрице диагональ из верхнего левого угла в правый нижний (единичная матрица). Пространство вне диагонали заполняется нулями. Например,

Было  Стало
0000  1000
0000  0100
0000  0010
0000  0001;

ZER — заполняет матрицу нулями; TRN — меняет местами ряды и колонки (транспонирует) в матрице. Ряд становится колонкой, а колонка представляется как ряд. Например,

Было  Стало
1111  1000
0100  1111
0100  1000
0100  1000;

INV — Строит обратную матрицу от заданной квадратной матрицы. Умножение обратной матрицы на исходную матрицу образует единичную матрицу (см. MAT IDN).

Остальные расширения и дополнения.

PARSE и PARSE$

Команда PARSE, подобно функции Split в VB6, разбивает строку на строчный массив, используя указанный символ (или последовательность символов) в качестве разделителя. В отличие от Split, автоматически создающего массив в переменной типа Variant, PARSE использует заранее подготовленный строчный массив. Подготовка заключается в задании количества элементов массива, которое можно узнать функцией PARSECOUNT.

Функция PARSE$ предназначена для получения только одного элемента массива, полученного разбиением строки через разделители. Например, в строке "Привет Мир!", используя в качестве разделителя пробел и указав возвратить первый элемент, вы получите слово "Привет".

BIT CALC intvar, bitnumber, calcexpr

Устанавливает или сбрасывает бит в источнике intvar по позиции bitnumber в зависимости от логического результата выражения. Примечательно то, что источником может быть массив, интерпретируемый как битовое поле.

В этом примере сбрасывается 96-й бит битового поля Fld128.

Dim Fld128(3) As Long
Bit Calc Fld128(0), 96, 1 And 2
MsgBox Bin$(Fld128(3))

BIT

Позволяет получать или задавать значение разряда


BIT(<Источник>, <НомерБита>)

Возвращает значение бита в "Источник" по позиции, указанной в "НомерБита". Источником может быть как число, так и выраженное через массив битовое поле.


BIT {SET | RESET | TOGGLE} Источник, НомерБита

В зависимости от второго ключевого слова устанавливает, сбрасывает или переключает бит в "Источник" по позиции "НомерБита". Источником может быть как число, так и выраженное через массив битовое поле.


DATA ["]Элемент["] [[, ["]Элемент["]] …]

Идентичен аналогичному оператору в ДОС-овских Бейсиках. Служит для хранения текстовых списков внутри программы. Работает в связке с READ$.


DATACOUNT

Сообщает количество элементов в списке, созданном оператором DATA. Предназначена для READ$


READ$

Считывает элементы списка, созданного оператором DATA в строчный массив. Количество элементов узнаёт с помощью DATACOUNT.


FILESCAN

Анализирует файл и возвращает количество файловых строк (разделённых CRLF) в режиме INPUT или динамических(OLE) строк в двоичном режиме. Динамические строки обычно сохраняются в файл (в двоичном режиме) методом PUT. Опционально может сообщить размер самой длинной строки.


PEEK

Функция чтения памяти в целочисленную переменную или строку. Эквивалент аналогичной функции в компиляторах Бейсик для ДОС, но с учётом специфики 32-битного адресного пространства. Применима только для чтения в адресном пространстве текущего процесса.


POKE

Функция записи в память из целочисленной переменной или строки. См. также PEEK.


REGEXPR и REGREPL (регулярные выражения)

Расширенная версия оператора Like в VB. Возвращает позицию и длину выражения, найденного по заданной маске. REGREPL дополнительно позволяет совмещать поиск и замену.


GRAPHIC

Группа команд, ориентированная на рисование графических объектов. Рисование может быть осуществлено в специальном элементе управления или в памяти. Э.у. для рисования представляет собой окно, которое создаётся командой CONTROL ADD GRAPHIC, а для рисования в памяти используется точечный рисунок, который может быть создан (GRAPHIC BITMAP NEW) или загружен (GRAPHIC BITMAP LOAD) соответствующей командой. Перед началом рисования необходимо указать активное окно рисования, на котором в последствии будут отображены все рисуемые объекты. Активное окно рисования устанавливается командой GRAPHIC ATTACH. Если вам нужно рисовать в разных окнах, то перед рисованием в каждом окне его нужно делать активным. Это относится и к рисунку в памяти. Рисование в памяти не видимо на экране, так как рисунок в памяти не привязан к какому либо из окон. Для отображения данных точечного рисунка, их нужно скопировать в активное окно рисования командой GRAPHIC COPY.


BIN$

Переводит число в строку, содержащую читабельное представление числа в двоичной системе счисления.


#STACK num_expr

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


#INCLUDE "ИмяФайла"

На место этой директивы при компиляции вставляется содержимое указанного в кавычках файла. Содержимое должно быть исходным кодом на языке PowerBasic. В Visual Basic вместо этого используются проекты, более подходящие для визульной концепции. В прошлых версиях языка Бейсик от Майкрософт (например QB) для объединения файлов использовалася аналогичный метаоператор REM $Include.


#RESOURCE "ИмяФайла. PBR"

Директива, указывающая имя файла ресурсов, который может быть вставлен в компилируемое приложение. В отличие от VB6 имеет свой формат, преобразовать в который можно утилитой (PowerBASIC Resource Converter), поставляемой вместе с PowerBasic.


HIBYT, HIINT, HIWRD и LOBYT, LOINT, LOWRD

Позволяет получить старшую или младшую половину переменной. Например из числа типа Long (4 байта) можно извлечь старший или младший Integer (2 байта).


MAKINT, MAKWRD, MAKLNG, MAKDWD, MAKPTR

Собирает значение из двух половин. Например Integer собирается из двух Byte, а Long из двух Integer.


EXP, EXP2, EXP10

Аналогичная VB функция, возводящая число в заданную степень.


ROTATE {LEFT | RIGHT}

Позволяет вращать биты в целом числе влево или вправо (в зависимости от второго ключевого слова).


SWAP

Обменивает значения между двумя переменными.


USING$

Аналог ДОС-овского варианта форматирования строки. В отличие от старых версий, выступает в качестве отдельной функции, а не приставки к оператору PRINT. Имеет более широкий набор возможностей, чем функция FORMAT.


DISKFREE и DISKSIZE

Позволяют узнать свободное место и общий объём диска.


ENVIRON

Расширенная версия функции ENVIRON в VB. Позволяет не только получать, но и устанавливать значения переменных среды (сноска).


FILENAME$

Позволяет узнать имя файла по его номеру


LPRINT {ATTACH, CLOSE, FLUSH, FORMFEED}

Операторы управления принтером. ATTACH — устанавливает связь с принтером используя в качестве идентификатора принтера имя порта (например LPT2) или сетевое имя принтера. При последующем использовании команды LPRINT будет использоваться этот принтер; CLOSE — Закрывает связь с принтером, открытую с помощью LPRINT ATTACH; FLUSH — Сбрасывает все печатаемые буферы и подаёт сигнал о начале печати. Используется в тех случаях, когда нужно начать печать немедленно, не ожидая окончания очереди печати. FORMFEED — подаёт принтеру сигнал извлечения страницы.


PRINTERCOUNT и PRINTER$

Используются для перечисления всех принтеров в системе и их характеристик.


SETEOF

Восполняет отсутствующую в VB функцию, позволяющую задавать конец файла (например обрезать файл).


PROFILE

Записывает на диск отчёт о времени, затраченном на выполнение процедур и функций. Помогает при поиске медленных участков программы.


FUNCNAME$

Возвращает имя процедуры или функции, в которой в данный момент выполняется программа.


CODEPTR

Расширенная версия оператора AddressOf в VB. Позволяет получать не только адреса процедур, но и адреса меток.

Сноски.

1 PowerBasic похож на Паскаль в том смысле, что он ориентирован на платформу операционной системы и микропроцессора.

3 Стоит отметить что практически все современные компиляторы языка Бейсик, за исключением VB и PB, довольно молоды и преимущественно находятся на стадии развития.

4 Сейчас эта компания известна в основном по системе программирования Delphi, но раньше она выпускала практически все известные компиляторы языков под собственной маркой Turbo: TASM (Ассемблер), TC (Си), TP (Паскаль), TB (Бейсик).

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

7 IBM/360 была системой из нескольких терминалов (дисплей и клавиатура), подключённых к одному большому процессору.

8 Необходимо помнить что в 16-разрядных ОС названия файлов и папок состоят из 8 символьного имени и 3 символов расширения (причём только латинские). Например, README.TXT. Соответственно длинные имена папок, такие как "Мои Документы" или "Program Files" не будут корректно обработаны компилятором.

9 Аппаратное средство защиты в процессоре Intel, позволяющее операционной системе ограничить доступ к ресурсам ЭВМ.

10 Старые технологии включаются в более новые и активизируются только когда необходимо запустить устаревшие приложения. Это позволяет запускать приложения, написанные для старых компьютеров на более современных. Например, программа для микропроцессора 8086 вполне работоспособна на Pentium 4, а программа для MS DOS работает в Windows NT. По сути, процессор Pentium 4 — это собранные в одной микросхеме все выпускавшиеся Intel микропроцессоры. Аналогично Windows’95 включает в себя полноценные Windows 3.1 и MS DOS.

11 На самом деле VB6 и PB/WIN загружают при запуске одинаковый набор библиотек: OLEAUT32, OLE32, ADVAPI32, GDI32, USER32 и KERNEL32. VB6 дополнительно к ним загружает в память библиотеку MSVBVM60.

Ссылки