Эта статья немного прольёт свет на то, из каких двух больших частей состоит Visual Basic, какая идейная подоплёка и какие люди стоят за появлением каждой из них на свет.
Как уже можно было догадаться из вышесказанного, есть два больших компонента. Это Ruby и EB. Пожалуй, наиболее удачное высказывание обо всём этом: Visual Basic является результатом «женитьбы» Ruby и EB — двух технологий, появившихся независимо друг от друга, и очень удачно подходивших друг к другу.
Ruby
Очень важное замечание, которое нужно сделать, прежде чем мы пойдём дальше: здесь и далее слово «Ruby» не имеет ничего общего с языком программирования Ruby и фреймворком Ruby on Rails. В конце концов, слово «ruby» переводится просто как «рубин».
(На фотографии: Алан Купер (Alan Couper))
Если вы когда-нибудь искали информацию о том, как появился на свет Visual Basic, то наверняка натыкались в википедии или где-то ещё на подобные слова:
май 1991 — выпущен Visual Basic 1.0 для Microsoft Windows. За основу языка был взят синтаксис QBasic, а новшеством, принесшим затем языку огромную популярность, явился принцип связи языка и графического интерфейса. Этот принцип был разработан Аланом Купером (Alan Cooper) и реализован в прототипе Tripod (также известном как Ruby). Первый Visual Basic был интерпретатором.
Из статьи о самом Алане Купере можно узнать, что он, в частности, известен как «отец Visual Basic». Англоязычная версия статьи немного более информативна.
На самом деле Алан Купер не работал в Microsoft, не состоял в команде по разработке VB — его заслуга в другом. Алан Купер придумал концепцию лёгкого создания визуальных оболочек и совместно с коллегами создал инструмент для построения визуальных оболочек. Напомню, что речь идёт о 80-х годах и времени, когда ОС с графическими интерфейсами только-только набирали популярность, а мейнстримом был DOS и другие ОС с текстовым интерфейсом.
Продукт, который сделала команда Алана Купера, как раз и назывался Ruby, и самое интересное, что это не был инструмент для программистов — это был инструмент для обычных пользователей без специальных навыков, дававший им, по замыслу Купера, возможность быстро слепить визуальную оболочку, которая бы облегчала какую-то работу. Эдакий конструктор визуальных интерфейсов для широкого круга лиц. Ох это время романтиков, считавших, что стоит дать людям компьютер — и каждый научится и станет собственноручно писать программы, чтобы заставить компьютер делать именно то, что нужно владельцу. Ruby не был ни языком программирования, ни интегрированной средой разработки (IDE) в современном понимании.
По рассказу Купера, когда он увидел Windows 1.0, он понял, что у этой платформы огромное будущее. Его приятно поразили две вещи: графический интерфейс и концепция DLL, позволяющая делать динамически конфигурируемые расширяемые системы, но одновременно с этим тогдашняя оболочка Windows (проводника и рабочего стола в современном понимании ещё не существовало) показалась ему просто ужасной. И тогда он решил написать свой идеальный shell для Windows. Общаясь со своими клиентами и пытаясь понять, каким должен быть идеальный shell, Алан Купер понял, что идеальной оболочки не существует. И что нужно дать пользователям инструмент, который позволит каждому из них сконструировать свой shell, заточенный под их потребности и их задачи, а не пытаться протолкнуть им некий идеальный shell, пытаясь при этом объяснить им, в чем его идеальность. Этот конструктор пользовательских оболочек изначально назывался Tripod, но позже был переименовал в Ruby, поскольку название «Tripod» много кем использовалось.
Позже Алан Купер показал свою разработку Биллу Гейтсу. Гейтсу концепция, что пользователь может рисовать визуальные элементы управления, размещать их где угодно на рабочем поле, перетаскивать, менять размеры и, главное, с элементами управления связывать какие-то действия, понравилась настолько, что он решил купить Ruby. Отныне Microsoft стала владелицей Ruby и определяла дальнейшую судьбу продукта.
В Microsoft решили поменять суть Ruby: оставляя концепцию лёгкого построения визуальных интерфейсов («нарисовал кнопку где нужно и быстро привязал к ней какое-то действие»), из инструмента по созданию визуальных оболочек (над уже существующими программами) с целевой аудиторией, состоящей из обычных пользователей, превратить его в инструмент по созданию новых программ с визуальными интерфейсами, целевой аудиторией которого были бы только программисты. Напомню, что исходно за Ruby помимо принципа визуального проектирования не стояло никакого языка программирования (была лишь простая система команд, набор которых был расширяем за счёт подключаемых DLL, так же как и набор элементов управления в палитре (Купер назвал их «gizmo», что в переводе на русский — «штуковина», этот термин сохранился и в VB6), который тоже расширялся за счёт сторонних DLL), и нужно было определиться, какой язык ляжет в основу нового инструмента. Но у Microsoft уже был QBASIC, и можно догадаться, какие именно планы родились в голове у Билла Гейтса, когда он принял решение купить такой перспективный актив, как Ruby.
В этом месте истории концепция и механизм построения визуальных интерфейсов, начисто лишённый какого-либо языка программирования за собой, должен был встретиться с языком программирования и механизмом, обеспечивающим его исполнение, начисто лишённым визуальной составляющей.
EB
Вы когда-нибудь смотрели, сколько разных недокументированных функций экспортирует рантайм VB?
Уж точно вы должны были слышать о функции EbExecuteLine, позволяющей интерпретировать и выполнить произвольную строку кода (правда лишь при работе под IDE). Вас не интересовало, что означает префикс «Eb» в именах всех этих функций? Как он расшифровывается?
Сейчас мы с этим разберёмся.
Я не уверен точно, какая именно из двух версий о двух возможных расшифровках более верная, но, очевидно, обе версии недалеки от правды. Начнём с первой версии и обратимся для этого к одной из статьей небезызвестного Джоэля Спольского.
Статья называется «My First BillG Review» («Моя первая проверка со стороны Билла Г.», есть русский перевод).
Краткий пересказ истории:
- Летом 1991-го Джоэль устроился на работу в Microsoft в отдел по разработке Excel. Одной из актуальных проблем Excel того времени было то, что в Excel был собственный убогий язык программирования для макросов, в котором не было глобальных и локальных переменных, не было процедур, был goto, но не было меток. Язык не имел собственного названия, и условно назывался «экселевские макросы».
- Добавить Variant-переменные, способные хранить значения любых других типов, иначе невозможно было бы в переменной сохранять значение ячейки Excel-таблицы (по крайней мере не прибегая к Select Case).
- Добавить позднее связывание (через IDispatch), потому что изначальная архитектура Silver требовала глубокого понимания системы типов, а в ней люди, пишущие макросы для Excel, не захотели бы разбираться.
- Добавить конструкцию For Each, позаимствованную из csh.
- Добавить конструкцию With ... End With, позаимствованную из Паскаля.
Задачей Джоэля было решить эту проблему. Витало мнение, что решение проблемы должно быть как-то связано с языком Basic. В другом отделе (в составе Microsoft) уже была наработка по созданию объектно-ориентированного Бейсика под кодовым названием «Silver». Менеджер команды «Silver» видел только одно применение для своей технологии — Excel. Джоэль убедил людей из команды Бейсиков, что команде Excel нужно нечто вроде Бейсика для Excel. При этом ему удалось настоять на том, что в язык должно быть было добавлено 4 фичи:
После чего Джоэль сел и написал спецификацию на будущий язык Excel Basic, которая заняла примерно 500 страниц, которая затем была отправлена на ознакомление/проверку Биллу Гейтсу. Дальнейшая часть статьи описывает состоявшуюся после этого встречу с Б. Гейтсом, на которой тот задавал Джоэлю вопросы на основе заметок на полях, оставленных Гейтсом при прочтении спецификации.
Финальная часть статьи говорит, что со временем положение дел сильно продвинулось, Excel Basic стал называться Visual Basic for Applications, а Гейтс однажды вспомнил, как непросто было нанять того толкового менеджера в команду Excel, который всем этим занимался.
Вторая версия расшифровки названия EB — это не Excel Basic, а Embedded Basic. По рассказу Скотта Фергюсона (который шутливо называет себя «мамой Visual Basic'а»), на рубеже 80-х и 90-х годов в Microsoft шла работа над СУБД «Omega» — проектом, который в итоге был заброшен. В состав СУБД «Omega» входил движок Embedded Basic (EB). Разработка шла в отделе, который назывался «отделом языков программирования для бизнеса» и занимался одновременно с этим разработкой QuickBASIC'а. Но основные ресурсы отдела «языков программирования для бизнеса» были сконцентрированы над созданием нового, объектно-ориентированного, заточенного под Windows бейсикоподобного языка программирования, который назывался Silver (мы уже встречали упоминание этого названия и этого продукта в рассказе Джоэля Спольского). Билл Гейтс в какой-то момент прислал в отдел «языков программирования для бизнеса» запрос на тему того, можно ли как-то скрестить Ruby и EB. Джон Файн (менеджер проекта на тот момент) совместно с Скоттом Фергюсоном были поставлены перед задачей дать ответ на этот запрос.
Была создана команда, которая занялась очень непростой интеграцией своего EB с только что купленным со стороны Ruby:
(Два человека на фотографии, чьи лица закрашены, были приглашены фотографом со стороны для улучшения композиции кадра, и к разработке VB никакого отношения не имеют)
Новый продукт, рождавшийся от слияния EB и Ruby, получил название Thunder (следы этого названия сохранились вплоть до VB6 в именах многих внутренних функций (например ThunderMsgLoop) или в именах оконных классов (например ThunderRT6FormDC). Скотт Фергюсон вспоминает, что ещё в январе 1989 Джон Файн написал (вероятно, начальству, возможно самому Гейтсу) предложение о создании языка программирования под названием «Visual BASIC». Это было ещё задолго до появления Ruby в Microsoft. Много позже перед самым релизом продукта Thunder это название воскресло из забытья наряду со многими другими названиями-кандидатами, рассматривавшимися в качестве официального названия продукта. И оно, на самом деле, многим не понравилось. Большинству нравилось название «Thunder» (в переводе — «Гром») с тогдашним слоганом «The Power to Crack Windows» (в переводе — «Мощь, распахивающая Окна» или, возможно, «Мощь, заставляющая дрожать Окна»).
Так или иначе, не столь важно, расшифровывать ли EB как Excel Basic или Embedded Basic. Судя по словам Джоэля о его вкладе в привнесение 4 важных нововведений в EB, которые остались в VB/VBA по сей день, его виденье истории и вклад в развитие становление VB и VBA тоже должен быть учтён.
Очевидно, что существовала технология Silver и движок EB, созданный изначально для СУБД «Omega».
EB сам по себе, не без участия Джоэля, вошёл в состав Excel и стал тем, что сейчас известно как VBA.
EB, слившись с куперовским Ruby, под влиянием идей Silver, стал тем, что сейчас известно как VB.
Проект «Omega» с имевшимся в нём EB, предположительно, реинкарнировал как Access/MS Jet, в котором EB по прежнему является значимой частью.
Современное положение дел
Вплоть до последней версии продукта — до VB6, граница между Ruby и EB не растворилась, оба этих компонента не слились до степени неразличимости. Между ними по прежнему есть отчётливая граница, каждый компонент занимается своим, имеет, так сказать, свою зону ответственности и область решаемых задач.
Разделение сохраняется даже на уровне исходников (сведения об исходниках, из которых скомпилирован VB, могут быть получены из файлов с отладочными символами). С точки зрения комплекта исходников, VB6 состоит из двух папок: ruby/ и vbadev/
В первой находятся все исходники Ruby, во второй — все исходники EB (который, как мы знаем из статьи Джоэля, в какой-то момент был переименован в VBA). У многих функций и переменных из Ruby в качесте префикса мы встретим префикс Rby, у многих функций, переменных и структур EB мы увидим префикс Eb.
EB — что включает в себя, чем занимается и за что отвечает:
- EB/VBA в целом устроен как технология, которую можно прикрутить к чему угодно — к любому внешнему приложению (для такого приложения применяется термин «host»/«хост»). Будучи приделанным к какому-то хосту, EB/VBA позволяет писать код, который может взаимодействовать с хостом, управлять объектной моделью хоста, что в итоге даёт возможность расширять логику работы хоста как угодно и писать автоматизацию для любых приложений-хостов, к которым приделали EB/VBA.
- EB/VBA не зависит от конкретного хоста — это хост зависит от EB/VBA, а EB/VBA может быть «прикручен» к чему угодно.
EB + Excel даёт нам VBA в Excel.
EB + Word даёт нам VBA в Word.
EB + AutoCAD даёт нам VBA в AutoCAD'е.
EB + Ruby даёт нам то, что мы знаем как «самостоятельный VB» (standalone VB, VB1—VB6).
То есть Ruby — лишь частный случай хоста для EB. Ruby не может жить без EB, зато EB может жить с любым другим хостом. - Редактор кода с подсветкой синтаксиса, автодополнением и IntelliSense — это часть EB/VBA. Речь идёт именно о самом текстовом поле, в котором пишется код, не включая панели инструментов, тулбары, меню. Именно по этой причине в VBA и VB редактор кода идентичен.
- Виртуальная машина, выполняющая P-код — часть EB/VBA.
- Механизм парсинга VB-кода и трансляции его в P-код по принципу just-in-time-компиляции — часть EB/VBA. Поэтому и синтаксис языка это тоже часть EB, и не имеет никакого отношения к Ruby, и по этой причине во всех хостах, к которым приделан EB, язык будет одним и тем же с одними и теми же синтаксическими правилами и особенностями (например баг Not Not Arr будет проявляться везде).
- Набор фундаментальных типов и набор поддерживаемых языком операторов (And, Xor, Like, &) — часть EB/VBA, поэтому к какому бы хосту ни был прикручен EB/VBA, всё это будет везде одинаковым. Если вы разработчик хоста и у вас есть полный контроль над исходниками хоста, но EB для вас — чёрный ящик, то вы никаким образом не сможете добиться появления поддержки нового оператора в коде (например, оператора побитового сдвига, которого исходно нет).
- Концепция обработки ошибок — часть EB/VBA, поэтому куда бы ни был прикручен EB/VBA, всё везде будет одинаковым.
- Концепция, что всё делится на проекты, а проекты состоят из модулей (точнее из project items — проектных элементов) проистекает из EB/VBA. При этом EB понятия не имеет ни о каких формах и UserControl-ах — с точки зрения EB модули бывают лишь двух видов (обычные и объектные). Хост может устанавливать свои подвиды для объектных модулей. В VB6 это классы, формы, юзерконтролы. В Excel это классы, юзерформы, листы (worksheet) и книги (workbook). В каких-то других хостах могут быть свои подвиды модулей. Но общая концепция проектов и модулей будет существовать в любом хосте.
- Концепция подключения ссылок на сторонних библиотеки типов — это возможность механизма EB/VBA, а потому она есть и в VB, и во всех применениях VBA. Сама же ссылка на подключенную к проекту TLB-шку внутри EB имеет название EBREF и описывается одноимённой структурой.
- Все функции, известные большинству как «функции рантайма», такие как MsgBox, InputBox, Rnd, Beep, CreateObject, DateDiff, RGB, StrReverse, MkDir — всё это часть EB/VBA, а потому все эти функции обязательно будут присутвовать не только в чистом VB, но и в VBA: и в Excel'е, и в Word'е, и AutoCAD'е и в любом другом хосте.
- Классы Collection и ErrObject являются частью EB/VBA, и, аналогичным образом, могут быть найдены и в чистом VB, и в приложениях Microsoft Office и прочих хостах.
- Такие недокументированные и поэтому мало кому известные механизмы, как сервер выражений и система мониторинга VBA-событий тоже являются частью EB.
- Всё, что так или иначе связано с пользовательским интерфейсом и какими-то визуальными проявлениями при выполнении программы — в принципе не_является частью EB. Например, функция MsgBox, являющаяся частью EB (VBA) и показывающая диалоговое окно с сообщением, не занимается непосредственно отображением окошка — EB понятия не имеет, каким способом должно быть показано сообщение и с применением каких средств это может быть сделано. Вместо этого EB обращается к своему хосту и вызывает у хоста специальную callback-функцию, которая отвечает за визуальную часть показа сообщений. Между EB и хостом существует такой интерфейс взаимодействия, как массив указателей на callback'и, предоставляемые хостом. Когда хост инициализирует EB, он вызывает у EB функцию EbInitHost и передаёт ей этот массив. EB запоминает указатель на этот массив и, когда приходит необходимость показать сообщение, берёт соответствующий элемент массива (этот адрес соответствующий callback-функции) и вызывает нужную функцию. И именно код хоста определяет, как будет показано сообщение, будет ли это окно, какое это будет окно и какое у него будет оформление. В этом плане EB сделан настолько универсальным, что хостом для EB может послужить, например, полноэкранная игра, использующая DirectX. И в этой игре обращение к MsgBox не приведёт к появлению обычного виндового диалога, который нарушит полноэкранный режим — реализованная игровым хостом callback-функция для отображения сообщений покажет сообщение в игровой стилистике, которое отображаться будет путём отрисовки с помощью DirectX, как и весь прочий игровой UI.
Ruby — что включает в себя, чем занимается и за что отвечает:
- В первую очередь надо сказать, что Ruby понятия не имеет (и его не волнует), какой язык стоит за кодом, который управляет инфраструктурой — той, что Ruby предоставляет. В случае VB в связке с Ruby используется EB, но чисто гипотетический там мог бы стоять и голый код на С/С++, или какой-нибудь скриптовый движок или же система визуального программирования типа Scratch, в которой код не пишут, а рисуют в виде блок-схем и диаграмм. Кстати, по изначальной задумке Алана Купера именно так Ruby и работал: когда пользователь нарисовал пару gizmo, скажем, кнопку и листбокс, то для того, чтобы сделать так, что при клике на кнопку листбокс исчезал бы, пользователь прямо мышкой рисовал стрелку, идущую от события «click» первого gizmo до метода «hide» второго gizmo. Итак...
- Все встроенные в VB элементы управления (контролы), такие как CommandButton, ListBox, Drive, Circle, PictureBox, ScrollBar — всё это часть Ruby.
- Объекты App, Clipboard, коллекция Forms, Printers, объект Screen, методы Load и Unload, метод LoadPicture/SavePicture — всё это часть Ruby. По этой причине, открыв VBA в Excel, у вас не получится написать там App.Path или Screen.ActiveForm — никаких App и Screen там попросту не будет, ибо это часть Ruby, а не EB.
- Механизм форм, на основе которых создаются окна — это Ruby.
- Вся обработка оконных сообщений, фильтрация, переадресация и превращение оконных сообщений в события у контролов — это Ruby.
- Значительная часть того, что касается среды разработки (IDE) и способов рисования форм в design-time — это Ruby. Например дерево проекта, палитра компонентов, панель свойств, диалог свойств проекта, диалоги среды, механизм поддержки подключаемых Add-in'ов — всё это Ruby.
- Все механизмы обеспечения, необходимые для работы самостоятельных EXE-файлов, а также работы проектов, скомпилированных с образованием DLL/OCX-файлов, а также ActiveX EXE-проектов — всё это Ruby. Например, любая ActiveX DLL обязана иметь функцию DllGetClassObject, которая возвращает фабрику класса для запрошенного класса. DllGetClassObject и фабрика классов — всё это реализовано в исходных файлах, являющихся частью Ruby.
- Всё, что касается OLE, DDE и механизмов data-binding'а (это когда контролы на форме, например, текстовые поля, привязываются к полям рекордсета и сами меняются при переходе по строкам в рекордсете, а изменение значений в контролах приводит к изменению значений в БД — и всё это без написания доп. когда со стороны VB-программиста, а просто за счёт настраивания связи между контролами формы и базой данных) — всё это Ruby.
Вот такое получается разделение. Если предположить, что у вас на руках есть полные исходники VB, и вам надо поправить что-то в поведении кнопки или добавить новое свойство у форм — идите в исходники Ruby. Если вы хотите поправить что-то во встроенном классе Collection или замахнулись на добавление нового оператора в VB — идите в исходники EB.
Разделение на Ruby и EB видно не только на уровне исходников. Если зайти в Project→References, то мы увидим там несколько подключенных библиотек типов, которые нельзя отключить. Одна из них соответствует всему, что привносит EB, другие две — всему, что привносит Ruby:
EB/VBA имеет свою библиотеку типов, и в ней можно найти все встроенные рантаймовые функции, а также два единственных класса — Collection и ErrObject:
Если мы откроем Visual Basic в Excel, мы увидим ту же библиотеку типов в References и Object Browser'е — и все функции будут на месте.
Ruby имеет свою библиотеку типов, где можно найти все встроенные контролы и объекты типа App, Printers, Screen:
Анатомия VB6.EXE, VBA6.DLL и MSVBVM60.DLL
Все знают, что скомпилированный VB-проект неминуемо будет зависеть от MSVBVM60.DLL — как только ни называют эту библиотеку (и рантаймом, и виртуальной машиной VB). Пока же проект существует в виде исходников и отлаживается под IDE, его работу обеспечивает сама IDE, состоящая главным образом из двух бинарников:
- VB6.EXE
- VBA6.DLL (от неё зависит VB6.EXE)
Факт, который кому-то может показаться удивительным: начинка msvbvm60.dll это наполовину начинка VB6.EXE, и наполовину начинка VBA6.DLL.
На самом деле VB6.EXE является результатом компиляции исходников Ruby, а VBA6.DLL является результатом компиляции исходников EB.
Рантайм MSVBVM60.DLL же просто состоит примерно наполовину из Ruby, и наполовину из EB.
Два комплекта исходников (ruby/ и vbadev/), олицетворяющих два основных компонента этого продукта, в результате компиляции и линковки с разными ключами и опциями дают в итоге 3 исполняемых файла.
То есть нет такого явления как «исходники MSVBVM60.DLL» и «исходники VB IDE» — и то, и другое компилируется и собирается из одних и тех же исходников! Но с разными опциями и ключами компиляции, с разными флагами условной компиляции.
Процесс сборки IDE (VB6.EXE + VBA6.DLL) получается таким:
Процесс сборки рантайм-библиотеки MSVBVM60.DLL почти такой же: те же самые исходные файлы компилируются (с немного другими ключами компиляции) в объектные файлы (obj), а затем всё это линкуется в один итоговый DLL-файл:
Подобно тому, как генетический код человека на 98% идентичен генетическому коду шимпанзе, подавляющая часть двоичного кода в составе msvbvm60.dll повторяет код либо из vb6.exe, либо из vba6.dll. Почти любая процедура, входящая в состав msvbvm60.dll является либо частью Ruby, либо частью EB, а потому может обнаружена в неизменном или слегка изменённом виде либо в составе vb6.exe, либо в составе vba6.dll.
Обратное не верно: суммарный размер кода vb6.exe (1.80 Мб) и vba6.dll (1.61 Мб) больше, чем размер msvbvm60.dll (1.32 Мб).
Очевидно, что в состав Ruby входит код, реализующий, например, редактор форм или диалог свойств проекта — всё это попадает в vb6.exe, но не попадает в msvbvm60.dll (оно там не нужно).
Очевидно, что в состав EB входит код, реализующий синтаксический разбор, анализ, компиляцию VB-кода в P-код — всё это попадает в vba6.dll, но не попадает в msvbvm60.dll (оно там не нужно).
Если сделать некоторые упрощения, и не ставить целью точно соблюдать масштаб, то следующая иллюстрация отлично показывает, как исполняемые файлы, образующие IDE (среду), и исполняемый файл, представляющий собой рантайм, состоят, на самом деле, из общего кода:
Выше уже были хоть и неполные, но достаточные списки вещей, являющихся частью Ruby, и являющихся частью EB. Если ещё больше упростить, отбросить лишнее и оставить только самые выдающиеся и часто вспоминаемые компоненты VB, то можно нанести их на эту иллюстрацию. Ещё раз хочу сказать: это сильное упрощённое изображение на базе сильно упрощённого текста. Его цель — не создание полного списка субкомпонентов, из которых состоят Ruby и EB. Полный список не уместится на такой маленькой картинке, картинку пришлось бы сделать как минимум раз в 20 больше. Цель в том, чтобы просто, взглянув на иллюстрацию, понять и почувствовать саму идею, сам принцип разделения. Получить примерное представление, какие субкомпоненты (отвечающие за те или иные вещи) являются частью Ruby, а какие частью EB, и как эти субкомпоненты распределены по исполняемым файлам (VB6.EXE, VBA6.DLL и MSVBVM60.DLL). Вот эта видоизменённая иллюстрация:
В следующих статьях мы более подробно поговорим о составе EB и Ruby, рассмотрим более полно набор компонентов, из которых складывается работоспособная среда VB IDE (ведь кроме VB6.EXE и VBA6.DLL есть ещё LINK.EXE, C2.EXE, MSO98RT.DLL и.п.).