Обзор продуктов компании TurboPower Software

Часть I. SysTools

Юрий Зачесов

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

Конечно, было бы замечательно, если бы все проблемы, возникающие при использовании вычислительной техники, решала операционная система. Но такого не бывает. Следующий уровень — готовые технические решения, например Microsoft Office. Если и они не отвечают поставленной задаче, требуется самостоятельная разработка, то есть программирование или конструирование. Желательно, чтобы выбранное средство разработки (Delphi, Visual Studio, Informix, «1C» и т.п.) содержало в себе все необходимые функции, но это выполнимо, как правило, только для простых проектов. Поэтому потребность в разработках сторонних фирм, пытающихся хотя бы частично решать унифицированные проблемы, по-прежнему остается актуальной. Именно такими разработками занимается американская фирма TurboPower (http://www.turbopower.com/; ftp://turbopower.com/pub).

Американская компания TurboPower Software (http://www.turbopower.com/) — разработчик программного обеспечения с большим стажем — традиционно специализируется на создании дополнений к средствам разработки компании Borland Software Corporation (бывшая Inprise Corporation). Продукты этой фирмы Turbo Professional и Object Professional известны со времён операционных систем DOS и Windows 3.11. В настоящее время фирма предлагает широкий набор инструментов, занимающих большой сегмент рынка общего и специального программного обеспечения. Если ваш бизнес — разработка программного обеспечения, применение продуктов TurboPower поможет вам сократить трудозатраты, связанные с разработкой, повысить надёжность программного обеспечения, сделать приложения более быстрыми. Более того, в ближайшее время, после появления Borland Kylix («Delphi для Linux») планируется появление продуктов TurboPower для операционной системы Linux.

 

В предлагаемом обзоре рассматриваются библиотеки процедур и классов TurboPower для Delphi. Однако всё сказанное ниже справедливо и для C++Builder.

Итак, на момент написания данной статьи (январь 2001 года) компания TurboPower предлагала следующие продукты, облегчающие программирование и решающие ряд типовых задач:

  • Abbrevia — набор компонентов, позволяющих добавлять архивирование файлов и сжатие данных в приложения пользователей. С помощью этого набора компонентов можно группировать файлы в архив и экономить место на диске, сжимая данные. Поддерживаются форматы сжатия данных PKZIP и CAB;
  • Async Professional — набор компонентов для обеспечения передачи данных с помощью факсов, модемов, а также FTP, SNPP, RAS, WinSock и др.;
  • Essentials — уникальный набор визуальных и невизуальных компонентов, которые могут использоваться в почти каждом приложении. Большинство компонентов можно просто положить на форму —  они требуют минимальной настройки;
  • FlashFiler — компактная серверная СУБД, не требующая универсальных механизмов доступа к данным. Позволяет создавать, переименовывать, удалять псевдонимы, таблицы и индексы, просматривать, редактировать, реструктурировать существующие таблицы, а также импортировать данные других СУБД;
  • Internet Professional — набор компонентов для создания Internet-приложений. Включает клиентские и серверные socket-компоненты, а также компоненты для поддержки протоколов POP3, SMTP, NNTP, HTML, SNTP, FTP, ICMP;
  • LockBox — набор компонентов, позволяющих добавить криптографическую защиту в 16- и 32-разрядные приложения. Использовать функции LockBox можно, не вдаваясь в сложности криптографической науки. В этом продукте учтены особенности информационной безопасности, основанные на самых последних международных стандартах шифрования; реализованы поддержка алгоритмов шифрования с открытым ключом, RSA, AES, DES, цифровой подписи и многого другого;
  • OnGuard — библиотека компонентов, классов и процедур, позволяющих защитить ваши продукты от несанкционированного копирования. При помощи OnGuard можно выпускать приложения с частичной функциональностью. Защита осуществляется с помощью ключей;
  • OfficePartner — уникальный набор компонентов, предназначенный для создания контроллеров автоматизации  Microsoft Office 97-2000. OfficePartner позволяет применять сервисы Microsoft Office в приложениях, созданных с помощью  Delphi и C++Builder, не вникая в особенности COM и автоматизации.  OfficePartner включает компоненты для доступа к объектам Microsoft Word, Microsoft Excel, Microsoft Outlook, Microsoft PowerPoint, VCL TdataSet, Office Assistant;
  • Orpheus — набор визуальных компонентов, существенно расширяющий возможности, предоставляемые компонентами, встроенными в Delphi и C++Builder;
  • Memory Sleuth — утилиты для контроля и анализа памяти и расходования ресурсов в 32-разрядных приложениях Delphi и C++Builder, которые также позволяют отслеживать корректность высвобождения ресурсов;
  • Sleuth QA Suite 2 — набор утилит для детальной отладки и профилирования приложений, поддерживающий компиляторы Borland и Microsoft. Sleuth ActionRecorder — самый новый инструмент для разработки профессиональных приложений, позволяющий записывать, управлять, прокручивать в обратном направлении входные последовательности событий. Sleuth CodeWatch — инструмент для проверки корректности обращения с ресурсами, позволяющий определить наличие утечки памяти и ресурсов в 32-разрядных приложениях. Sleuth StopWatch —  анализатор выполнения приложения (профайлер) — позволяет определять «узкие места» при выполнении программы. Sleuth LineProfiler — сообщает о том, на что тратится большая часть времени выполнения приложения. Указывает время выполнения каждого оператора программы. Sleuth CoverageAnalyst — помогает найти участки кода, в которых возможно появление ошибок.
  • SysTools — библиотека классов и компонентов, содержащая классы для  манипулирования данными, классы-контейнеры, классы для организации эффективного управления сетью, классы управления потоками, процедуры с эффективным алгоритмом доступа к данным.

В данной статье мы рассмотрим библиотеку SysTools 3.

Библиотека классов SysTools 3 впервые появилась в 1997 году. Последняя ее версия была выпущена в 2000 году. Стоимость продукта при заказе его на Web-сайте TurboPower Software — 249 долл. Этот набор компонентов и классов содержит сотни проверенных и оптимизированных процедур, позволяющих ускорить разработку, что позволяет надеяться на достижение максимального быстродействия приложений, созданных с его применением.

  1. Классы манипулирования данными:
    1. Библиотека процедур и функций для работы со строками (теперь с поддержкой Unicode).
    2. Библиотека процедур и функций для выполнения арифметических операций повышенной точности.
    3. Библиотека процедур и функций для работы работы с данными типа «дата» и «время».
    4. Библиотека процедур и функций для финансовых и статистических расчётов.
    5. Библиотека математических и статистических процедур, подобных встроенным в  Microsoft Excel.
    6. Компоненты и классы для создания почтовых клиентов с поддержкой протокола MIME (шифрование/дешифрование).
    7. Библиотека процедур и функций, обеспечивающая целостность данных благодаря использованию вычисления контрольной суммы.
    8. Библиотека процедур и функций, применяемых в астрономии.
    9. Библиотека процедур и функций, расширяющая возможности манипулирования текстовыми файлами.
    10. Класс сортировки слиянием.
  2. Контейнерные классы:
    1. Класс TstBits — поддерживает операции с битами.
    2. Класс TstCollection — имитирует класс коллекций из средства разработки Borland Pascal 7.
    3. Класс TstContainer — контейнерный класс, облегчающий разработку сложных структур данных.
    4. Класс TstDictionary — строковый словарь.
    5. Класс TstDQue — наследник класса TstList, базовый класс для создания структур данных типа очередей и стеков.
    6. Класс TStHashTable — похож на TstDictionary, поддерживает доступ к хэш-таблицам (быстрый алгоритм доступа к данным).
    7. Класс TStLArray — поддерживает одномерный массив большой размерности (аналогичный типу array of… в Delphi) для 16-разрядных приложений.
    8. Класс TStList — двухсвязный линейный список.
    9. Класс TStLMatrix — двухмерный массив (матрица).
    10. Класс TStSortedCollection — наследует от TstCollection, коллекция с добавлением функции сортировки.
    11. Класс TStTree — бинарное сбалансированное дерево.
    12. Класс TStVMatrix — наследник TStLMatrix, обеспечивает дополнительные функции контроля при сохранении данных на диске.
    13. Невизуальные компоненты, частично объединяющие вышеперечисленные классы и позволяющие упростить их применение (рис. 2).
  3. Библиотека системных функций (их около 100) из модуля STUTILS, позволяющих устанавливать, сбрасывать и проверять битовые флаги; обмениваться различными типами данных; инициализировать массивы начальными значениями без выполнения цикловых операций. Кроме того, эти функции обеспечивают доступ к файловой и системной информации; поддерживают арифметику с указателями, а также класс TstRegIni, функционально основанный на классах Delphi TIniFile и TregIniFile, но полностью переписанный так, что при его использовании не имеет значения — с INI-файлом или системным реестром производятся какие-либо действия.
  4. Классы сетевого управления из модуля STNET позволяют создавать и использовать сетевые группы как со стороны сервера, так и со стороны клиента, использовать возможности ограничения прав доступа, предоставляемые Windows NT/2000, устанавливать сетевые соединения, посылать сообщения, обеспечивать сетевым соединениям дополнительные возможности.
  5. Визуальные и невизуальные компоненты (будут подробно описаны ниже).
  6. Классы потоков, которые имеют как самостоятельную ценность, так и широко используются для обеспечения функциональности внутри SysTools.

Таблица иллюстрирует примеры приложений, представленных  в каталоге EXAMPLES.

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

Несмотря на многообразие классов, для обеспечения их функциональности в SysTools применяются унифицированные программные решения. Во всех классах, если это необходимо, присутствуют свойства Compare, DisposeData, Equal, LoadData, StoreData  — все типа «функции». При помощи этих свойств, без порождения наследников от классов, можно обеспечивать нужную функциональность объектов.

Например, для класса быстрой сортировки свойство Compare устанавливает функцию, используемую для определения порядка следования элементов. После создания класса этому свойству должна быть присвоена переменная типа «функция», позволяющая определить порядок элементов. Вы можете без риска изменять свойство Compare этого класса, только когда другое его свойство  Count = 0, то есть сразу после вызова метода Create  или после вызова метода Reset. В случае ошибки возникает исключение EStSortError с кодом ошибки stscBadCompare.

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

type  
  PersonRecord = record  
  First : String[20];  
  Last  : String[30];  
  Age   : Integer;  
end;  
     
function MyCompare(const E1, E2) : Integer; far;  
var  
  P1 : PersonRecord absolute E1;  
  P2 : PersonRecord absolute E2;  
begin  
  Result := CompareText(P1.Last, P2.Last);  
  if Result = 0 then  
    Result := CompareText(P1.First, P2.First);  
  if Result = 0 then  
    Result := P1.Age-P2.Age;  
end;  
     
begin  
  MySorter:=TStSorter.Create(MinimumHeapToUse(SizeOf(PersonRecord)) * 4,  
                               SizeOf(PersonRecord));  
  MySorter.Compare := MyCompare;  
  ...  
end.  

Класс TStSorter может сортировать до двух миллиардов элементов.  Элементом может быть значение типизированной переменной, запись или объект. Цифровая сортировка, применяемая в SysTools, использует нерекурсивный алгоритм quicksort сортировки слиянием. Quicksort — широко используемый, самый быстрый универсальный алгоритм сортировки. Сортировка слиянием –является стандартным методом сортировки большого количества элементов, которые не обязательно должны умещаться в оперативной  памяти.

Однако относительно элементов, которые можно сортировать с помощью цифровой сортировки, имеются некоторые ограничения. Во-первых, общее число сортируемых элементов ограничено количеством свободного пространства на жестком диске. В процессе сортировки может потребоваться дополнительная оперативная память, объем которой может достигать удвоенного количества сортируемых элементов. Правда, обычно это значение равно примерно 1,5,  а может быть и нулевым, если все данные умещаются в оперативной памяти. Во-вторых, размер каждого элемента ограничен 65 535 байтами в 16-разрядных приложениях и MaxLongInt (2147483647) байтами в  32-разрядных приложениях. Класс TStSorter использует многопоточность и критические секции, чтобы защитить свои методы Put, Get, и Reset.

Другой пример — свойство StoreData класса «словарь», которое позволяет определить процедуру записи данных в поток (в файл).

Когда контейнерный класс пишет сам себя в поток, сначала пишутся данные, описывающие его собственное состояние, а затем данные объекта. Чтобы сделать это, вызывается процедура StoreData для каждого экземпляра объекта данных. Именно эта подпрограмма решает, как и что записать на диск (кроме информации о состоянии объекта данных). Следующий пример подпрограммы StoreData извлечен из типового проекта EXCOLL (форма проекта представлена на рис. 1).

ARecord = record 
  First : S10; 
  Last  : S15; 
  Age   : Integer; 
end; 
… 
procedure MyStoreData(Writer : TWriter; Data : Pointer); far; 
begin 
  with ARecord(Data^), Writer do begin 
    WriteString(First); 
    WriteString(Last); 
    WriteInteger(Age); 
  end; 
end; 
… 
MyContainerClassObject.StoreData := MyStoreData; 
… 

Параметр Data — нетипизированный указатель, передаваемый процедуре MyStoreData. Поля данных из записи записываются в поток методами объекта Writer (WriteString и WriteInteger).

Класс TSt Dictionary  из модуля StDict реализует строковый словарь. Функциональность класса напоминает обычный словарь с книжной полки. Элемент находится по имени. Если поиск успешен, то доступна любая информация, отнесенная к этому имени; в противном случае считается, что данных в словаре нет. Предусмотрена возможность добавлять новые элементы к словарю или удалять существующие. Однако в отличие от обычного словаря двойные вхождения элементов (дубликаты) не допускаются.

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

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

property DisposeData : TDisposeDataProc 
TDisposeDataProc = procedure (Data : Pointer); 

Параметр Data может быть указателем на объект или  запись. Допускается вложенность. Например, при использовании TStCollection или TstDQue в качестве полей элемента словаря иногда необходимо освободить память, зарезервированную для этих объектов. Методы Clear и Destroy вызывают процедуру, присвоенную  свойству DisposeData. Присвоение значения должно происходить при создании класса. Если свойству ничего не присваивается, ошибки не будет, но и при вызове методов Clear и Destroy память, выделенная под элементы, не будет освобождена.

Свойство словаря Equal устанавливает функцию сравнения, которая используется для поиска в словаре.

property Equal : TStringCompareFunc 
TStringCompareFunc = function(const String1, String2 : string)    : Integer; 

Функция сравнения вызывается при поиске элемента много раз. Это происходит при вызове методов Add, Delete, Exists, Join и Update. Заданная по умолчанию функция сравнения  поиск не осуществляет. Если необходимо сделать поиск чувствительным к регистру или пользоваться различными функциями сравнения (отличными от функций по умолчанию), этому свойству следует присвоить  новую функцию.

Функция сравнения должна возвратить значение –­ 1 (или любое другое — меньшее 0), если String1 меньше String2, 0, если строки равны, и 1 (или любое другое значение больше 0), если String1 больше String2. Следующий пример устанавливает свойство в определяемую пользователем функцию сравнения.

MyDictionary.Equal := MyEqualComparisonFunction; 

Еще один интересный алгоритм, представленный в SysTools, реализован в классе двоичного дерева поиска (модуль STTREE). Дерево используется для сохранения сортируемой коллекции объектов данных в памяти. Дерево может расти до любого размера (пока хватает оперативной памяти), без предварительно установленных пределов. Каждый объект данных в дереве имеет свой ключ, который используется классом для сопоставления элементов. Как только объекты данных добавлены к дереву, они могут быть просмотрены в возрастающем или убывающем порядке. Поскольку порядок объектов данных известен, нахождение нужного объекта данных — операция быстрая, фактически эквивалентная двоичному поиску. В каком-то смысле модуль STTREE предлагает комбинацию возможностей модулей STDICT и STSORT. Подобно строковому словарю двоичное дерево поиска может быстро находить объекты данных, хотя и немного медленнее, чем класс словаря. Основное преимущество двоичных деревьев поиска состоит в том, что объекты данных сохранены в сортируемом порядке.

Двоичное дерево поиска — динамический контейнер, который может сокращаться и расти в зависимости от числа элементов. Другим преимуществом, по сравнению со строковым словарем, является отсутствие необходимости задавать размер хеш-таблицы. Кроме того, можно рассмотреть двоичное дерево поиска как альтернативу по отношению к модулю STSORT, когда нужен упорядоченный набор объектов данных.

Сортировка с использованием двоичного дерева поиска осуществляется при добавлении новых объектов данных. Сформировать двоичное дерево, которое совершенно не сбалансировано, довольно просто. Например, если узлы добавляются к стандартному двоичному дереву поиска в отсортированном порядке, дерево вырождается в список связей, где каждый «родитель» имеет точно одного «ребенка». Используя алгоритм двоичного дерева поиска из SysTools, этой проблемы можно избежать, балансируя дерево во время операции вставки и удаления объектов данных. В классе используется алгоритм балансирования AVL-tree; это гарантирует, что глубина любого левого перехода отличается не больше чем на один шаг от глубины правого перехода. Класс TSTTREE является наследником класса TSTCONTAINER. Данные в дереве сохраняются в узлах, основанных на объекте TSTTREENODE или его потомке. Узлы сохранены в отсортированном порядке. Дерево автоматически сбалансируется во время выполнения методов Insert и Delete.

Класс TSTTREE — абстрактная структура данных в том смысле, что она «не знает», какие данные содержит каждый элемент в дереве и как вычислять значения ключа или отношения упорядочения для элементов. Передача этой информации классу осуществляется путем назначения подпрограммы сравнения для свойства Compare.

На класс TSTTREE накладывается одно ограничение  — дубликаты не разрешаются. При попытке добавить дубликат возникает исключение ESTCONTAINERERROR с кодом ошибки stscDupNode. Этого можно избежать, используя метод Find перед добавлением нового элемента. Приведенный ниже фрагмент кода иллюстрирует этот способ.

… 
{search for duplicate entry, if found - don't try to add} 
      TN := MyTree.Find(PR); 
      if TN = nil then 
        MyTree.Insert(PR); 
… 

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

Во многих приложениях применяются операции, которые необходимо повторять в циклах, например:

for I := 1 to 100 do 
   X := X + SomeArray[I]; 

В некоторых контейнерных классах SysTools сохранена возможность такого обращения к элементам множества (поддерживается классами TStLArray, TStLMatrix и TStVMatrix), однако для некоторых классов такой способ обращения может оказаться чрезвычайно неэффективным. Например, в классе TstList можно, забыв о двойных связях, добраться до каждого элемента, просматривая каждый раз весь список по связям в одном направлении — начиная с начала списка. Однако лучше для этого использовать метод Iterate, который для всех контейнерных классов, включая массивы, реализует самый эффективный метод перебора для своего класса. У метода Iterate также имеется ограничение. Функция Action, передаваемая ему как параметр, не может переупорядочивать, вставлять или удалять элементы из множества, с которым работает класс. Классы TStList, TStTree и TstDictionary используют тип функции:

TIterateFunc = function(Container : TStContainer;Node : TStNode; 
                                         OtherData    : Pointer) : Boolean; 
Для TstCollection : 
TCollIterateFunc = function(Container : TStContainer; Data : Pointer; 
                                                  OtherData : Pointer) : Boolean; 
Для TstBits : 
TBitIterateFunc = function(Container : TStContainer; N : LongInt; 
                                                 OtherData : Pointer) : Boolean; 

Все методы Iterate  выполняют аналогичные действия. Метод вызывает функцию Action для каждого элемента данных в контейнере. Этот процесс продолжается до тех пор, пока функция Action возвращает true или пока не закончится перебор всех данных в контейнере. Если функция Action возвращает false для какого-то элемента данных, метод Iterate заканчивается и возвращает информацию относительно последнего элемента данных (тот, который заставил функцию Action возвратить false). Возвращенная информация зависит от типа контейнера.

И методы Iterate, и функции Action, которые разрабатывает пользователь, имеют параметр OtherData. Этот параметр — нетипизированный указатель на некоторые данные для метода Iterate, который, в свою очередь, передает его в функцию Action. Если в этом нет необходимости, в качестве параметра передают nil. Использование параметра OtherData избавит от необходимости применения глобальных переменных для передачи информации в функцию Action.

Другим параметром в функции Action –является указатель на данные (то есть данные элемента, с которыми Action работает как с Node^.Data).

if TMyRecord(Data^).Field1 < 0 then 
  {do something} 

Следующие примеры из EXCOLL показывают два способа использования функции Action: первый находит определенную запись; второй перебирает коллекцию и добавляет данные в список.

type 
ARecord = record 
    First : String[10]; 
    Last  : String[15]; 
    Age:  : Integer; 
  end; 
  
function MatchCollString(Container : TStContainer; Data : Pointer; 
                                              OtherData : Pointer) : Boolean; far; 
{this function returns true until the First and Last fields test to be identical}  
begin 
 Result:=(CompareText(ARecord(Data^).First,ARecord(OtherData^).First)    <> 0) or 
            (CompareText(ARecord(Data^).Last,    ARecord(OtherData^).Last) <> 0); 
end; 

Data — указатель на запись в коллекции. OtherData — указатель на запись с критериями поиска. Параметры First и Last передаются через OtherData, чтобы можно было принять решение о том, находится данный человек в списке или нет. CompareText — нечувствительная к регистру функция сравнения строк из Delphi. Пока функция Action возвращает true, метод Iterate будет перебирать элементы множества.

function CollWalker(Container : TStContainer; Data : Pointer; 
                                      OtherData : Pointer) : Boolean; far; 
{this function always returns true so it visits all nodes in the collection}  
begin 
  with ARecord(Data^) do 
    STDlg.LB1.Items.Add(First + ' ' + Last + ', ' + IntToStr(AR.Age));  
  Result := true; 
end; 

Эта функция при помощи приведения типов обращается к полям записи по адресу Data. Она всегда возвращает True, заставляя  метод Iterate перебрать все данные. Все это используется следующим образом:

var 
  PN : TStListNode; 
  ARec : ARecord; 
begin 
  MyCollection := TStList.Create(TStListNode); 
  ... 
  {set fields in ARec for search, pass ARec to the Action function} 
  PN := MyCollection.Iterate(MatchCollString, true, @ARec); 
  ... 
  MyCollection.Iterate(CollWalker, false, nil); 
  ... 
  MyCollection.Free; 
end. 

При вызове метода Iterate функцию MatchCollString передают с помощью свойства  Action — как функцию, которую необходимо вызывать для каждого элемента данных. Второй параметр определяет порядок перебора списка. True — сортировка в возрастающем порядке. Третий параметр — указатель на данные, сравнения с которыми будут сделаны. Метод Iterate возвращает указатель на последний элемент данных, посещенный в списке, то есть объект данных, для которого MatchCollString возвратил False. Другими словами, PN указывает на элемент данных, соответствующий набору критериев поиска в  ARec. Если возвращается указатель PN = nil (пустое значение), значит ничего не было найдено.

Второй вызов метода Iterate получает функцию CollWalker в качестве значения свойства Action. Поскольку эта функция всегда возвращает True, каждый узел в списке будет посещен. Второй параметр установлен в False так, что список будет сортироваться в убывающем порядке. Поскольку потребности в дополнительных данных нет, третий параметр Iterate — nil.

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

При инсталляции SysTools в палитру компонентов Delphi добавляются две дополнительные закладки, которые называются SysTools и SysTools(СС).

Компоненты, изображенные на рис. 2 и находящиеся на странице SysTools, — в основном визуальные или обеспечивающие разнообразную поддержку других визуальных компонентов (всего их 26). На странице SysTools(СС) (рис. 3) собраны компоненты, инкапсулирующие контейнерные классы, то есть невизуальные. Так как мы уже довольно подробно обсудили контейнерные классы и классы манипулирования данными, остановимся теперь на компонентах из SysTools.

Визуальные и невизуальные компоненты, представленные на рис. 2, можно условно разбить на шесть подклассов.

  1. Компоненты для создания пользовательского интерфейса, которые позволят легко добиться функциональности, сходной с функциональностью Windows Explorer.
  2. Невизуальные HTML-компоненты, позволяющие переводить тексты в Web-документы.
  3. Невизуальный компонент, поддерживающий работу с регулярными выражениями.
  4. Невизуальный компонент, осуществляющий контроль над арифметическими выражениями.
  5. Невизуальный компонент, который помогает приложению обрабатывать системное сообщение WM_COPYDATA и позволяет организовывать работу нескольких экземпляров приложения.
  6. Невизуальный компонент, обеспечивающий запуск одного приложения из  другого.

Теперь рассмотрим более подробно каждую из составляющих. Компоненты для построения оболочки включают визуальные и невизуальные составляющие, которые инкапсулируют многие функции из Windows API. Предлагаемая функциональность в основном аналогична функциональности Windows Explorer. Копирование файлов с мультипликацией динамики процесса, запросы на подтверждение операций, которые могут привести к потере данных или появлению новых каталогов, отображение диалога форматирования дисков, возможность создания ярлыков для файлов, поддержка операций перетаскивания (drag and drop) —  вот короткий перечень функций, которые могут появиться в приложениях, использующих компоненты TstBrowser, TstCustomShellController, TstDropFiles, TstFileOperation, TstFormatDrive, TstShellAbout, TstShellComboBox, TstShellEnumerator, TstShellListView, TstShellNotification, TstShellTreeView, TstShortcut, TStTrayIcon, TstVersionInfo и классы TstShellFolder, TStShellFolderList, TstShellItem, TstShellItemList. Смотрите пример № 1 проекта, частично реализующий возможности выше перечисленных компонентов.  

Всё это работает, конечно, только для 32-разрядных приложений, но получается с такой легкостью, что создает иллюзию всемогущества. На рис. 4 показаны главное окно демонстрационного приложения и результат выполнения одной из функций.

Невизуальный компонент StFileToHTML объединяет классы TStStreamToHTML и TStFileToHTML, позволяет конвертировать текстовые файлы в HTML-страницы с учетом индивидуальной разметки. Классы разрабатывались для перевода исходных текстов на Pascal и C++ в HTML-страницы, однако их возможно использовать и для форматирования любых данных, основанных на ключевых словах и стилях. Единственное ограничение —  наличие строк во входных данных. Свойства класса TStStreamToHTML, отвечающие за механизм форматирования, можно изменять как на стадии проектирования, так и во время выполнения, что придает методам конвертирования дополнительную гибкость. В примере № 2 демонстрируются работа компонента для конвертирования исходного текста в HTML-страницу, в этом же каталоге лежит вариант выходного файла (out1ext1). На рис. 5 представлена форма демонстрационного приложения.

Невизуальный компонент StRegEx объединяет классы TStStreamRegEx и TstRegEx и позволяет, используя алгоритм регулярных выражений в стиле UNIX, осуществлять поиск и замену подстрок в строках. При этом обеспечивается гибкий механизм определения условий. Если вы ранее никогда не сталкивались с регулярными выражениями, то будете удивлены, как при помощи столь коротких шаблонов можно задавать такие сложные поисковые условия.

Использование этого компонента позволит легко организовывать обработку файлов, созданных в других (не Windows-подобных) операционных системах, а также решать другие задачи, связанные с обработкой регулярных выражений. Кстати, строка "*.*", которую часто используют в диалоговых окнах для поиска всех файлов (ищем файлы с любыми именами и любыми расширениями), является примером регулярного выражения.

Невизуальный компонент TstExpression обеспечивает обработку арифметических выражений на нескольких уровнях. На самом низком уровне могут быть описаны простые математические выражения. На более высоком уровне можно определять цифровые константы для использования в арифметических выражениях. И наконец, можно добавлять определенные пользователем функции и методы.

Для описания арифметических выражений применяются следующие грамматические правила:

выражение:=<терм|выражение+терм|выражение-терм> 
терм:=<терм|терм*фактор|терм/фактор> 
фактор:=<база|база^фактор> 
база:=<число без знака|(выражение)|знак фактор|вызов функции> 
число без знака:=<цифра|цифра.цифра|цифра Е| цифра.цифра Е > 
знак:=<+|-> 
вызов функции:=<идентификатор| идентификатор(параметры)> 
параметры:=<выражение|параметры,выражение> 
Е:=<е число|е знак число> 
число:=< цифра |число цифра> 
идентификатор:=<начинается с A..Z,_ следующие символы A..Z,_,0..9> 
цифра:=<0..9 > 

Используется традиционный порядок выполнения операций. Помимо арифметических операций в класс, поддерживающий этот компонент, включены многие тригонометрические, логарифмические и другие математические функции. Компонент TstExpression очень гибок. Вы можете легко добавлять в выражения поддержку своих функций. Например, чтобы добавить поддержку для функции _Sin(), следует, во-первых, описать функцию с надлежащим числом и типом параметров (ключевое слово far для 32-разрядных приложений может быть опущено).

function _Sin(Value : TStFloat) : TStFloat; far; 
begin 
  Result := Sin(Value); 
end; 

Затем при помощи метода экземпляра класса TstExpression необходимо добавить к TstExpression компоненту вышеописанную функцию.

MyStExpression.AddFunction1Param('sin', _Sin); 

Аналогичная техника допустима и для методов класса.

Визуальный компонент TstExpressionEdit — наследник компонента TEdit, к которому добавлены один новый метод, два свойства и два новых события, обеспечивает вычисления по вышеописанной грамматике за счёт нововведений. В примере № 3 демонстрируется работа компонента TstExpressionEdit. Обратите внимание на то, что в приложении, форма которого приведена на рис. 6, для обеспечения функциональности на форму был помещен только компонент TstExpressionEdit без каких бы то ни было дополнительных действий.

Таким образом, благодаря паре компонентов  TstExpression и TstExpressionEdit можно не только создавать приложения-калькуляторы, но и расширять возможности арифметических выражений, используемых в этих приложениях.

Процедуры и функции из модуля StFirst позволяют перехватить сообщение WM_COPYDATA. Положив на форму невизуальный компонент TStWMDataCopy, можно заманить в ловушку это специальное сообщение.

Функция IsFirstInstance позволяет определить, выполняется ли уже экземпляр данного приложения; если выполняемое приложение найдено, возвращает false. Тогда можно использовать процедуру ActivateFirst, передавая ей в качестве параметра строку с информацией о втором экземпляре приложения, или процедуру ActivateFirstCommandLine, которая получает в качестве входного параметра строку параметров приложения при повторном вызове. Еще проще использовать компонент TStWMDataCopy. С помощью события OnDataReceived можно обработать любую последовательность событий, появление повторных экземпляров приложений. Для идентификации событий используются системные объекты — мьютексы. Отметим, что Windows 3.x не поддерживает сообщение WM_COPYDATA. Фрагмент кода из фирменного примера exfirst показывает один из способов обработки такого события.

procedure TfrmEXWDC.StWMDataCopy1DataReceived(Sender: TObject; 
  CopyData: tagCOPYDATASTRUCT); 
var 
  S : string; 
  I : Longint; 
begin 
  {$IFDEF WIN32} 
  S := String(PChar(CopyData.lpData)); 
  {$ELSE} 
  S := StrPas(PChar(CopyData.lpData)); 
  {$ENDIF} 
  ShowMessage(S); 
  I := pos(' ', S); 
  if (I = 0) then 
    I := pos(#9, S); 
  if (I > 0) then begin 
    S := Copy(S, I+1, Length(S)); 
    DoFileOpen(S); 
  end; 
end; 

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

SysTools содержит компонент TStBarCode, поддерживающий штрих-коды в соответствии со следующими спецификациями: UPC A, UPC E, EAN 13, EAN 8, Interleaved 2 of 5, Codabar, Code 11, Code 39, Code 93 и Code 128. Компонент TStPNBarCode (специализированный штрих-код, используемый американской почтовой службой) выделен в отдельный модуль STBARPN. Существуют в SysTools и аналоги компонентов с поддержкой баз данных. Все форматы штрих-кодов тщательно протестированы. На рис. 7 приведена форма демонстрационного приложения EXBARC.

В заключение несколько слов о классах ввода/вывода. Классы потоков (TstBufferedStream, TstAnsiTextStream, TStMemoryMappedFile) — важная составная часть SysTools. Наследование идет от общего предка TStream через класс TMEMORYSTREAM к классу TFILESTREAM. Потоки используются во многих классах внутри самой библиотеки  SysTools. Чтение и запись в файлы форм и ресурсов выполнены с помощью интерфейса потока.

В стандартных классах ввода/вывода Delphi имеются некоторые любопытные особенности. Хотя класс TFILESTREAM поддерживает доступ к стандартным файлам, он не осуществляет никакой буферизации. Еще одной любопытной особенностью потоков является то, что они не позволяют организовать доступ к данным в потоке — как к строкам, так и знакам.

SysTools обеспечивает эти два типа функциональных возможностей в TSTBUFFEREDSTREAM и TSTANSITEXTSTREAM соответственно. Они  действуют как фильтр потока, делегирующего функции чтения, записи и поиска другому потоку, но обеспечивающего некоторые дополнительные функциональные возможности в начале его обработки. .

Кроме этих двух классов потока SysTools обеспечивает инкапсуляцию функций Win32 для файлов, проецируемых в память, и разделяемой памяти, которые, по существу, обеспечивают буферизацию потоков на уровне операционной системы. В этом есть определенный недостаток — нужно решить заранее, как должен расти большой файл. Файлы, проецируемые в память, являются механизмом разделения памяти между двумя различными процессами с автоматической поддержкой синхронизации.

Обзор продуктов фирмы TurboPower, часть I (SysTools — системные инструменты), не претендует на полноту описания. Но я старался, чтобы, по крайней мере, все, что есть в библиотеке SysTools, было хотя бы один раз упомянуто. Содержимое библиотеки вытекает из ее названия (SysTools — системные инструменты). Думаю, оно может заинтересовать широкий круг пользователей. Меня больше всего заинтересовали контейнерные классы, что и объясняет их более подробное рассмотрение в данном обзоре. Но SysTools содержит много других интересных решений. Например, операции с данными на уровне битов или класс TstString,  добавляющий функциональность к стандартным строковым процедурам и функциям, а кого-то, возможно, заинтересует библиотека астрономических процедур. Удобной особенностью библиотеки SysTools является то, что ее компонентами можно пользоваться дифференцированно. То, что SysTools сопровождается исходными текстами, написанными в хорошем стиле, делает описываемый продукт заслуживающим широкого использования.

КомпьютерПресс 5'2001