С самого рождения (или чуть позже) операционная система Windows использовала библиотеки динамической компоновки DLL (Dynamic Link Library), в которых содержались реализации наиболее часто применяемых функций. Наследники Windows - NT и Windows 95, а также OS/2 - тоже зависят от библиотек DLL в плане обеспечения значительной части их функциональных возможностей.
Рассмотрим ряд аспектов создания и использования библиотек DLL:
как статически подключать библиотеки DLL;
как динамически загружать библиотеки DLL;
как создавать библиотеки DLL;
как создавать расширения МFC библиотек DLL.
Использование DLL
Практически невозможно создать приложение Windows, в котором не использовались бы библиотеки DLL. В DLL содержатся все функции Win32 API и несчетное количество других функций операционных систем Win32.
Вообще говоря, DLL - это просто наборы функций, собранные в библиотеки. Однако, в отличие от своих статических родственников (файлов. lib), библиотеки DLL не присоединены непосредственно к выполняемым файлам с помощью редактора связей. В выполняемый файл занесена только информация об их местонахождении. В момент выполнения программы загружается вся библиотека целиком. Благодаря этому разные процессы могут пользоваться совместно одними и теми же библиотеками, находящимися в памяти. Такой подход позволяет сократить объем памяти, необходимый для нескольких приложений, использующих много общих библиотек, а также контролировать размеры ЕХЕ-файлов.
Однако, если библиотека используется только одним приложением, лучше сделать ее обычной, статической. Конечно, если входящие в ее состав функции будут использоваться только в одной программе, можно просто вставить в нее соответствующий файл с исходным текстом.
Чаще всего проект подключается к DLL статически, или неявно, на этапе компоновки. Загрузкой DLL при выполнении программы управляет операционная система. Однако, DLL можно загрузить и явно, или динамически, в ходе работы приложения.
Библиотеки импортирования
При статическом подключении DLL имя.lib-файла определяется среди прочих параметров редактора связей в командной строке или на вкладке "Link" диалогового окна "Project Settings" среды Developer Studio. Однако.lib-файл, используемый при неявном подключении DLL, - это не обычная статическая библиотека. Такие.lib-файлы называются библиотеками импортирования (import libraries). В них содержится не сам код библиотеки, а только ссылки на все функции, экспортируемые из файла DLL, в котором все и хранится. В результате библиотеки импортирования, как правило, имеют меньший размер, чем DLL-файлы. К способам их создания вернемся позднее. А сейчас рассмотрим другие вопросы, касающиеся неявного подключения динамических библиотек.
Согласование интерфейсов
При использовании собственных библиотек или библиотек независимых разработчиков придется обратить внимание на согласование вызова функции с ее прототипом.
Если бы мир был совершенен, то программистам не пришлось бы беспокоиться о согласовании интерфейсов функций при подключении библиотек - все они были бы одинаковыми. Однако мир далек от совершенства, и многие большие программы написаны с помощью различных библиотек без C++.
По умолчанию в Visual C++ интерфейсы функций согласуются по правилам C++. Это значит, что параметры заносятся в стек справа налево, вызывающая программа отвечает за их удаление из стека при выходе из функции и расширении ее имени. Расширение имен (name mangling) позволяет редактору связей различать перегруженные функции, т.е. функции с одинаковыми именами, но разными списками аргументов. Однако в старой библиотеке С функции с расширенными именами отсутствуют.
Хотя все остальные правила вызова функции в С идентичны правилам вызова функции в C++, в библиотеках С имена функций не расширяются. К ним только добавляется впереди символ подчеркивания (_).
Если необходимо подключить библиотеку на С к приложению на C++, все функции из этой библиотеки придется объявить как внешние в формате С:
extern "С" int MyOldCFunction(int myParam);
Объявления функций библиотеки обычно помещаются в файле заголовка этой библиотеки, хотя заголовки большинства библиотек С не рассчитаны на применение в проектах на C++. В этом случае необходимо создать копию файла заголовка и включить в нее модификатор extern "C" к объявлению всех используемых функций библиотеки. Модификатор extern "C" можно применить и к целому блоку, к которому с помощью директивы #tinclude подключен файл старого заголовка С. Таким образом, вместо модификации каждой функции в отдельности можно обойтись всего тремя строками:
#include "MyCLib.h"
В программах для старых версий Windows использовались также соглашения о вызове функций языка PASCAL для функций Windows API. В новых программах следует использовать модификатор winapi, преобразуемый в _stdcall. Хотя это и не стандартный интерфейс функций С или C++, но именно он используется для обращений к функциям Windows API. Однако обычно все это уже учтено в стандартных заголовках Windows.
При запуске приложение пытается найти все файлы DLL, неявно подключенные к приложению, и поместить их в область оперативной памяти, занимаемую данным процессом. Поиск файлов DLL операционной системой осуществляется в следующей последовательности.
Каталог, в котором находится ЕХЕ-файл.
Текущий каталог процесса.
Системный каталог Windows.
Если библиотека DLL не обнаружена, приложение выводит диалоговое окно с сообщением о ее отсутствии и путях, по которым осуществлялся поиск. Затем процесс отключается.
2.1.3.Библиотеки динамической компоновки – DLL. COM-модель и COM-объекты.
DLL
Файлы DLL (Dynamic Link Library, библиотека динамической компоновки) являются основой программной архитектуры Windows и отличаются от исполняемых файлов фактически только заголовком. Правда, это не означает, что если переименовать DLL-файл, то он станет исполняе-мым: имеется в виду заголовочная информация файла.
Для загрузки операционной системы необходимо запустить файл, имеющий размер всего 20 Кбайт. Как легко догадаться, в файл такого раз-мера невозможно поместить код, реализующий всю ту гигантскую работу, которая производится по ходу выполнения любого приложения. Этот файл является загрузчиком ядра операционной системы, физически размещен-ным в нескольких DLL-файлах.
Помимо кода, DLL-файлы могут хранить данные и ресурсы. Например, при изменении значка (ярлыка) пользователю предлагается на выбор набор значков из файла SHELL32.DLL.
Для создания любой программы Windows, имеющей соб-ственное окно, в проекте необходимо подключать как минимум два модуля: Windows и Messages. Первый из этих файлов содержит прототипы функций API и GDI. Посмотрим на прототип одной из них:
function CreateDC; external gdi32 name "CreateDCA";
Здесь величина gdi32 - константа, описанная в этом же модуле:
Const gdi32 = "gdi32.dll";
Таким образом, функция CreateDC физически размещена в файле gdi32.dll и каждое приложение, использующее функции GDI, обращается к этой библиотеке.
Приблизительно так же выглядят прототипы остальных функций и про-цедур, но указываемые в прототипе имена библиотек индивидуальны для каждой из них.
Приложение не умеет ни создавать окно, ни выводить в него информацию и не содержит кода для этих действий. Все запущенные приложения (клиен-ты) передают соответствующие команды файлу gdi32.dll (серверу), который отрабатывает их, осуществляя вывод на устройство, ссылку на контекст ко-торого передается в качестве первого аргумента функции. При этом клиен-тов обслуживает единственная копия сервера в памяти.
Помимо важности DLL как основы программной архитектуры операцион-ной системы, представление о динамических библиотеках необходимо иметь каждому разработчику программных систем. Многие программные системы строятся по следующему принципу: основной код и данные размещаются в динамических библиотеках, а исполняемому файлу отводится роль загруз-чика.
Иногда программисту бывает необходи-мо просмотреть список функций и процедур, размещенных в конкретном файле DLL. Для этого можно воспользоваться утилитой tdump.exe, поставляемой в составе Delphi (в каталоге bin). Для ее использования скопируйте ее и необходимый dll-файл в отдельный каталог и запустите утилиту с параметрами <имя анализируемого файла> и <имя файла-результата>, например:
TDUMP.EXE gdi32.dll gdi.txt
В файле gdi.txt будет выдана информация, извлеченная утилитой из заголовка библиотеки. Информация группируется по секциям, среди которых наиболее часто программисту требуется секция экспортируемых функций для уточне-ния содержимого библиотеки.
Итак, чаще всего DLL представляет собой набор функций и процедур. Как говорится в справке Delphi по DLL, "динамические библиотеки являются идеалом для многоязыковых проектов". Это действительно так: при исполь-зовании DLL совершенно безразлично, в какой среде созданы сама биб-лиотека и вызывающие ее модули.
К одной библиотеке, как правило, может обращаться одновременно несколько приложений. Библиотеку в такой схеме называют сервером , а обслуживаемое им приложение – клиентом. Сервером и клиентом в общем случае могут являться и библиотека, и приложение. В частности, это означает, что некоторая библиотека, в свою очередь, может "подгружать" функции из другой библиотеки.
Приведем несложный пример библиотеки:
library MyDLL; //проект библиотеки. Файл должен иметь то же имя.
//описание экспортируемой функции
procedure MySquare (var x: integer); export; stdcall;
exports //список экспортируемых функций
//блок инициализации библиотеки
После компиляции образуется файл MyDLL.DLL. Таким образом, сервер готов. Теперь создадим клиента:
procedure MySquare (var x:integer); stdcall; external "MyDLL.dll";
ShowMessage(IntToStr(x));
В коде клиента указывается имя вызываемой функции, но во время работы клиент при вызове динамической библиотеки ориентируется не по имени функции, а по соответствующей ей точке входа, адрес которой он получает при инициализации библиотеки. В том виде, как это приведено в примере, библиотека подключается сразу после запуска программы-клиента. Такая организация взаимодействия называется статическим связыванием .
Динамическое связывание отличается тем, что клиент загружает библиотеку не сразу же после своего размещения в памяти, т.е. запуска, а по мере надобности. Приведем пример динамического связывания:
// процедурный тип подгружаемой функции
type TMySquare = procedure (var x:integer); stdcall;
procedure TForm1.Button1Click (Sender: TObject);
hcDLL: THandle; //указатель на библиотеку
ProcMySquare: TMySquare; //подгружаемая функция
hcDLL:=LoadLibrary("MyDLL.dll"); //Динамически загружаем DLL
if hcDLL<=HINSTANCE_ERROR then //Проверка на наличие библиотеки
ShowMessage("Не найдена библиотека MyDLL.dll.");
//библиотека загружена. Получаем адрес точки входа функции.
ProcMySquare:=GetProcAddress(hcDLL, "MySquare");
if not Assigned(procMySquare) then //Проверка на наличие функции
ShowMessage("Функция не найдена.");
procMySquare(x);
ShowMessage(IntToStr(x));
FreeLibrary(hcDLL); //Выгружаем библиотеку
Схема действий теперь такова: загружаем библиотеку только в тот момент, когда она действительно необходима, получаем адрес требуемой функции, связываем его с соответствующей переменной процедурного типа, и лишь потом обращаемся к ней. Обратите внимание, что успешная загрузка библиотеки не является признаком того, что мы можем успешно использовать нужную нам функцию. Это требуется проверять дополнительно, поскольку в общем случае разные по функциональному составу библиотеки могут иметь одинаковое имя. И, конечно, как это водится у порядочных программистов, после завершения работы с библиотекой сообщаем системе, что мы в библиотеке больше не нуждаемся и ее можно выгрузить, освободив ресурсы. Правда, это не означает, что библиотека на самом деле будет выгружена – система регистрирует все подключения к библиотеке (в том числе и из других программ) и выгрузит ее только тогда, когда все подключения будут освобождены.
Динамическая загрузка библиотек используется очень часто. Это связано, прежде всего, с соображениями экономии ресурсов памяти и ускорения загрузки программ. Действительно, зачем занимать лишнюю память библиотеками, функции которых могут и не понадобиться за все время работы приложения. Не говоря уже о том, что на загрузку многочисленных библиотек и проверку корректности функциональных связей уходит дополнительное время, в течение которого приложение просто не может приступить к работе.
COM-модель
Технология, основанная на динамических библиотеках, является очень эффективной, поэтому и стала основой программной архитектуры операционной системы. Однако, ей присуще ограничение, не позволяющее использовать парадигму объектно-ориентированного программирования (ООП): библиотеки DLL "в чистом виде" могут содержать код функций и процедур, а также ресурсы, но не способны содержать описание классов. По мере развития программирования как технологии, возникла необходимость поддержки ООП на уровне операционной системы.
Самым ходовым примером такого использования идей ООП на уровне системы являются составные документы. Вставляя в текстовый документ электронную таблицу или записывая в нем математическую формулу с помощью редактора формул, пользователь как раз и встречается со зримым воплощением ООП. Вставленный, внедренный документ является объектом со своими методами и свойствами. Это пример зримого воплощения технологии COM (Component Object Model, модель компонентных объектов). Хотя в примере и приведены только составные документы, COM представляет концепцию взаимодействия программ любых типов: библиотек, приложений, системного программного обеспечения и др.
Первоначально для всей группы технологий, в основе которых лежит COM, корпорацией Microsoft было предложено общее имя -– OLE. Затем по мере развития и дополнения технологии, это название менялось. В настоящий момент укрепилось название ActiveX, но программисты со стажем часто по привычке так и продолжают пользоваться термином OLE для обозначения данной группы технологий.
Важно уяснить, что COM – это не язык, не протокол. Это метод взаимодействия между программами и способ создания программ.
Функции программы, доступные для использования другими приложениями, называются сервисами . COM определяет стандартный механизм, с помощью которого одна часть программного обеспечения предоставляет свои сервисы другой.
Для нас особенно важным является то, что технология COM также является независимой от языка программирования. Физически приложение, предоставляющее сервисы, может быть реализовано в виде обычного выполнимого модуля, либо, чаще всего, в виде библиотеки. Как и в случае обычных библиотек DLL, неважно, в какой программной системе созданы серверы и использующие их клиенты. В случае с обычной DLL-библиотекой клиенту достаточно знать адрес точки входа нужной функции и в определенный момент передать управление по этому адресу. Тот факт, что библиотека должна предоставлять не обычные функции, а методы объектов, внес в эту схему некоторые изменения.
COM-объекты
COM-объекты представляют собой двоичные программные компоненты, подобные невизуальным компонентам Delphi, устанавливаемые на уровне операционной системы и доступные для использования в любой среде программирования. Основное отличие COM-объектов от других состоит в том, что у них нет свойств, а есть только методы. К тому же, еще одно отличие состоит в методах использовании конструкторов и деструкторов.
Для создания COM-объекта не вызывается функция конструктора, как для обычного объекта в Delphi. Первым действием в этом случае идет создание главного объекта , который имеет методы, использующиеся для создания других объектов и получения необходимых интерфейсов.
Для удаления COM-объекта вместо метода Free обычно предназначен метод _Release.
Интерфейсом обозначается набор функций, предоставляемый некоторым приложением. Обычные приложения предоставляют один интерфейс, т.е. весь тот набор функций, который реализован в программе, является в такой терминологии единым интерфейсом. Если же программа предоставляет несколько наборов функций, то говорят, что она имеет несколько интерфейсов.
Сервер поддерживает один или несколько интерфейсов, состоящих из методов. Клиенты могут получать доступ к серверу только через вызовы методов интерфейсов объекта. Иного непосредственного доступа к данным у них нет.
Все COM-интерфейсы унаследованы от интерфейса, называемого IUnknown, обладающего тремя методами: QueryInterface, AddRef и _Release.
Последний в этом списке метод мы уже вскользь обсуждали – удаление объекта.
Предпоследний метод предназначен для подсчета ссылок на интерфейсы. Клиент явно инициирует начало работы экземпляра COM-объекта, а для завершения его работы он вызывает метод _Release. Объект ведет подсчет клиентов, использующих его, и когда количество клиентов становится равным нулю, т.е. когда счетчик ссылок становиться нулевым, объект самоуничтожается. Это сделано для избежания преждевременного уничтожения объекта. Дело в том, что клиент, получив указатель на интерфейсы объекта, способен передать один из них другому клиенту без ведома сервера. В такой ситуации ни один из клиентов не может закончить работу объекта с гарантией того, что делает это не преждевременно. Пара методов AddRef и _Release дает гарантию того, что объект исчезнет только тогда, когда никто его не использует.
Обычно свой первый указатель на интерфейс объекта клиент приобретает при создании главного объекта. Имея первый указатель, клиент получает указатели на другие интерфейсы объекта, методы которых ему необходимо вызвать, запрашивая у объекта эти указатели с помощью метода QueryInterface.
Завершая обзор COM-модели, нужно подчеркнуть, что она является развитием технологии "традиционных" DLL, позволяющей использовать парадигму ООП на уровне операционной системы, функций API.
2.2.Обзор среды программирования Borland Cbuilder
2.2.1.Отличия Borland CBuilder и Borland Delphi. Особенности синтаксиса языка C++ в Borland Cbuilder.
(лекция не читалась)
2.2.2.Особенности разработки приложений в среде CBuilder
(лекция не читалась)
3.Технологии компьютерной графики
3.1.Разработка графических приложений без использования специализированных библиотек
3.1.1.Методы ускорения построения 2D изображений. Динамическая запись в видеопамять устройства.
(лекция не читалась)
3.1.2.Работа с изображениями. Форматы графических файлов (BMP, JPEG, GIF). Чтение и запись графических файлов.
(лекция не читалась)
3.2.Разработка графических приложений с использованием специализированных библиотек
3.2.1.Обзор библиотек OpenGL и DirectX
На протяжение многих лет среди программистов идет спор о преимуществах использования того или иного интерфейса для создания графики в компьютерных играх и других графических приложениях. Главные соперники в этой области - библиотеки OpenGL и DirectX. Индустрия до сих пор так и не сделала однозначный выбор в пользу того или иного API.
Общие сведения о OpenGL
Стандарт OpenGL (Open Graphics Library - открытая гра-фическая библиотека) был со-здан и утвержден в 1992 году ведущими фирмами в области разработки программного обеспечения как эффективный аппаратно-независимый интер-фейс, пригодный для реализации на различных платформах. Основой стандарта стала биб-лиотека IRIS GL, изначально разработанная фирмой Silicon Graphics Inc (SGI).
С начала 90-х годов OpenGL использу-ется в различных областях ин-дустрии и науки. Архитектура библиотеки получилась на-столько удачной, что уже на протяжении более десяти лет она остается стабильной и предсказуемой. OpenGL де-факто является стандартом в области программирования графики. Но в этом скрыт и ее недостаток. Комитет по пересмотру архитектуры (ARB) работает довольно медленно – любое изменение стандарта требует множества согласований, оформления документации и т.д. Как следствие, OpenGL развивает-ся очень медленно. Правда, до недавнего времени с этим не было особых проблем, поскольку изна-чально библиотека предназна-чалась для быстрых рабочих станций профессионального уровня, которые обновляют не так уж часто. Однако сейчас даже дешевые видеокарты за $100 превзошли уровень профессиональных монстров пяти-летней давности стоимостью в тысячи долларов. При этом об-новление их возможностей происходит в среднем раз в год. Фактически OpenGL не по-спевает за индустрией, и, что-бы получить доступ к новей-шим функциям видеокарт, иг-ровые разработчики вынужде-ны использовать так называе-мый механизм расширений (extensions).
На данный момент OpenGL прошла путь от версии 1.0 всего до версии 1.4. "Революционная" версия 2.0 находится в процессе стан-дартизации.
Общие сведения о DirectX
К моменту выхода Windows 95 большинство игр по-прежнему делалось под MS-DOS. Windows в те времена не пред-оставляла никаких возможностей для программирования игр. Много-численные уровни абстракции (введенные в целях совмести-мости и универсальности) де-лали доступ к звуковому и ви-деооборудованию весьма мед-ленным и неприменимым для игровых приложений. Поэтому было решено разработать биб-лиотеку, предоставляющую возможность прямого доступа к аппаратуре. Это позволило бы играм работать на приемлемой скорости и, как следствие, увеличило бы про-дажи Windows 95.
Вместо создания собствен-ного API Microsoft использовала разработку небольшой компа-нии RenderMorphic. По неофициальной версии, изначально API был выполнен авторами в рамках студенческо-го задания и в конечном итоге провалился на экзамене. Тем не менее, Microsoft интегрировала эту библиотеку в свой Game SDK (игровой комплект разработки). Корпорация подавала это как идеальное решение для программирования игр.
Однако то, что позже стало называться DirectX 1.0, не при-обрело широкой популярности. Библиотека оказалась медлен-ной, с большим количеством ошибок, с неудобной архитекту-рой и, кроме того, чрезмерно сложной.
Разумеется, Microsoft не со-биралась сдаваться и продол-жила развитие библиотеки с учетом пожеланий разработчи-ков игр. Первой более или ме-нее жизнеспособной версией была DirectX 3.0. Позже после-довали версии 5, 6 и 7 (четвер-той не было), Седьмая версия была воспринята разработчика-ми с интересом: она хорошо работала, ее интерфейсы были достаточно удобны в использовании. Восьмая версия при-несла интересные нововведения – вершинные и пиксельные шейдеры (специальные, обычно короткие программы, предназначенные для выполнения на графическом процессоре; ис-пользуются для расчета осве-щения, создания тех или иных спецэффектов и т.п.). Не-давно вышедший DirectX 9 явился развитием этого перспективного направления.
Длительное время DirectX рассматривалась как неудачная альтернатива OpenGL. Однако последние улучшения в API сделали эту библиотеку весьма мощной и стабильной. По-скольку она разрабатывается авторами ОС, можно утверждать, что скорость ее рабо-ты с графикой оптимальна. Многие считают, что именно DirectX, а не OpenGL, становит-ся стандартом для программи-рования графики. Microsoft по-стоянно работает в тесном кон-такте с разработчиками аппаратуры, обеспечивая поддержку новых возможностей. Более того, DirectX иногда предлагает различные возмож-ности раньше, чем на рынке появляются видеокарты с их аппаратной реализацией.
Кроме того, DirectX, помимо собственно графики, предлагает интерфейсы для работы со звуком, источникам ввода, мультимедиа и т.д.. У OpenGL таких функций нет - это чисто графическая библиотека.
Архитектура
Ключевая особенность OpenGL – простота. Ядро OpenGL конт-ролирует процесс обработки примитивов (т.е. треуголь-ников). Для передачи данных используется процедурная мо-дель, фактически – вызовы функций. В каждый момент времени состояние OpenGL оп-ределяется через набор пере-менных, задающих параметры обработки (например, наклады-вать текстуру или не накладывать, использовать источник освещения или нет и т.п.). Каждый новый передан-ный треугольник проходит об-работку в соответствии с теку-щим состоянием. Такой меха-низм весьма эффективен, а код обычно короток и прост. Хотя ядро OpenGL процедурное, в использовании OpenGL совме-стно с объектно-ориентиро-ванными технологиями слож-ностей обычно не возникает: все зависит от выбора про-граммиста.
Структура DirectX очень сильно отличается от OpenGL. DirectX основан на модели COM (Component Object Model). Как следствие, в отличие от простого вызова функций эта модель предполагает выполнение дополнительных действий, связанных с компонентной архитектурой DirectX. Такая архитектура имеет как достоинства, так и недостатки. В частности, код, в котором используются вызовы DirectX, обычно трудно назвать легко читаемым и понимаемым. Даже рисование простого треугольника требует огромного объема кода. Раз-работчики Microsoft, конечно, понимают это, поэтому для уп-рощения программирования ими создана отдельная биб-лиотека DirectX Common Files, которая скрывает часто ис-пользуемый код. Однако, даже она не спасает ситуацию.
Хотя архитектура DirectX сильно отличается от OpenGL, в их развитии все более за-метны тенденции к сближе-нию. Такая ситуация возникает прежде всего потому, что обе библиотеки предназначены для эффективной работы с ап-паратурой, и чем ближе их структура будет к аппаратной реализации, тем меньше времени будет уходить на преобразование ко-манд пользователя в команды аппаратуры.
Производительность
Вопрос производительности настолько же важен, насколько запутан и неясен. Дебаты на тему "Что быстрее - OpenGL или DirectX?" не утихают. При этом, как ни парадоксально, скорость обоих библиотек оди-накова.
Иначе и быть не может, по-тому что сейчас большинство функций реализованы напря-мую через аппаратные ускори-тели. Естественно, производи-тельность может различаться в зависимости от степени опти-мизации программного кода и используемой для тестирова-ния аппаратной платформы. Оптимизация аппаратных драй-веров тоже может внести свой вклад в преимущество той или иной библиотеки. Такие вещи достаточно тяжело предвидеть, поэтому хорошие игровые "движки" часто имеют две вер-сии: под OpenGL и под DirectX. Это приводит к тому, что срок разработки увеличивается, стоимость ее возрастает и, естественно, появля-ются ошибки. Однако, таковы реа-лии современного рынка.
Сравнение
В чем же, основное различие между биб-лиотеками? Прежде всего – в удобстве интерфейса, функциях, гибкости, перспективах разви-тия и области применения.
Начнем с функций. В последнее время все чаще появляются заявления вроде "DirectX 9 под-держивает пиксельные шейдеры, a OpenGL не поддерживает, поэтому все игры должны быть написаны под DirectX!" Такое мнение верно только отчасти. Действительно, если посмот-реть на стандарт OpenGL по-следней версии (1.4), в нем ни слова о шейдерах.
Много лет назад OpenGL разрабатывалась как библиоте-ка, которая оставалась бы акту-альной в далеком будущем. К сожалению, это будущее при-шло и благополучно ушло в прошлое, а неповоротливость Комитета по пересмотру архитектуры не позволяет надеяться на оперативное включение в библиотеку новых опций. Для решения этой проблемы в OpenGL существует уже упоминавшийся механизм расширений, с помощью кото-рого можно использовать раз-личные функции, не входящие в базовую спецификацию и поддерживаемые только реали-зацией OpenGL для конкретной видеокарты.
Этот процесс выглядит сле-дующим образом: как только производитель выпускает ви-деокарту с поддержкой опреде-ленной полезной функции, он включает ее в свою реализа-цию OpenGL (которая обычно входит в поставку драйвера). Для программиста эта возмож-ность становится доступной, если он специальным образом запросит данное расширение. Конечно, такой путь не универсален: на другой видеокарте сделать это наверняка не получится из-за различий в интерфейсе. Поэтому существуют расшире-ния, одобренные ARB, – при их использовании можно надеять-ся, что они будут работать на видеокартах различных произ-водителей. Такие расширения являются кандидатами на вклю-чение в последующие версии OpenGL.
Данный механизм является не-удобным. В DirectX все проще: функциональность, либо под-держивается, либо не поддер-живается данной версией биб-лиотеки. Если нет – придется надеяться и ждать следующей версии. Однако случаи, когда аппаратные функции не ис-пользуются из-за того, что не были включены в версию DirectX, довольно редки – как уже говорилось, Microsoft тесно работает с производителями аппаратуры. С другой стороны, ждать версий DirectX приходится око-ло года, а в это время новые функции уже доступны через расширения OpenGL.
Таким образом, по под-держке аппаратных функций OpenGL и DirectX, в общем, эк-виваленты. OpenGL новые функции доступны через меха-низм расширений, а в DirectX они появляются только в новых версиях.
DirectX очень удобен для любителей объектно-ори-ентированного программирования и СОМ. СОМ в DirectX используется для внесения изменений в библиотеку (в новых версиях) без изменения существующего кода. В OpenGL такого нет, но это вряд ли можно назвать серьезным не-достатком. И вот почему.
Объем кода, необходимого для написания простой про-граммы на DirectX, весьма ве-лик (варьируется от 200 до 800 строк). Microsoft активно пыта-ется уменьшить этот показа-тель, но пока ее усилия особого успеха не приносят. В OpenGL все существенно проще - для решения такой же задачи необходимо менее 50 строк кода.
Серьезным достоинством OpenGL является, прежде всего, то, что это "открытый стан-дарт". Любая компания, имею-щая аппаратную платформу, может купить лицензию у SGI и затем сделать собственную реализацию OpenGL. Измене-ния в OpenGL предлагаются, обсуждаются и утверждаются представителями различных компаний. Что касается DirectX, то здесь ситуация пря-мо противоположная. Только Microsoft может вносить какие-либо изменения в библиотеку. Иначе говоря, именно Microsoft в конечном итоге оп-ределяет все пути развития библиотеки, и если путь был выбран неверно, это может быть исправлено только в но-вой версии.
Итак, достоинства библиотек становятся наиболее очевидны при их использовании в разных (но в то же время пересекаю-щихся) прикладных областях. DirectX идеален для профессио-нальной разработки игр и муль-тимедийных приложений на платформе Windows. OpenGL ис-пользуется на высокопроизводи-тельных рабочих станциях, в на-учной сфере, в образовании, а также в любых проектах, где требуется переносимость приложений на различные программные или аппаратные платформы. Кроме того, OpenGL применяется и для написания игровых "движков", правда, в последнее время на этом по-прище его теснит DirectX.
Перспективы развития
Появление графических про-цессоров (GPU - Graphics Processing Unit) нового поколения, позволяющих создавать доселе немыслимые спецэффекты в реальном времени, всколыхнуло индустрию: всем стало ясно, что за GPU будущее. Очевидно, что графические библиотеки должны соответствовать этой тенденции.
Начиная с восьмой версии, DirectX имеет встроенную поддержку программируемых шейдеров. В девятой версии эта поддержка была значительно улучшена и расширена. В дальнейшем развитие DirectX наверняка будет связано с постепенным улучшением гибкости и удобства использования программируемой аппаратуры. Каких-либо радикальных перемен пока не ожидается. Например, вряд ли стоит надеяться, что Microsoft сделает реализации DirectX на других платформах.
В OpenGL поддержка программируемой графической аппаратуры была добавлена на уровне расширений. Однако архитектура библиотеки изначально не была рассчитана на использование подобных конструкций, поэтому эти расширения выглядят слишком инородно.
В то же время близится к завершению разработка нового стандарта библиотеки – OpenGL 2.0. Развитие графической аппаратуры вышло за пределы исходной спецификации. Вторая версия OpenGL призвана поднять планку и вновь создать стандарт для компьютерной графики на десятилетия. Помимо прочего OpenGL 2.0 включает в себя возможность программирования всего графического конвейера на языке высокого уровня (подобный язык уже появился в девятой версии DirectX и называется HLSL – High Level Shading Language).
Библиотеки динамической компоновки (DLL)
DLL (англ. Dynamic Link Library -- «библиотека динамической компоновки», «динамически подключаемая библиотека») в операционных системах Microsoft Windows и IBM OS/2 -- динамическая библиотека, позволяющая многократное использование различными программными приложениями. K DLL относятся также элементы управления ActiveX и драйверы. В мире UNIX аналогичные функции выполняют так называемые общие объекты.
Формат файлов DLL придерживается тех же соглашений, что и формат исполняемых файлов, сочетая код, таблицы и ресурсы, отличаясь лишь интерпретацией некоторых полей.
Библиотеки динамической компоновки также имеют способность обеспечивать обмен данными между процессами. Когда в рамках DLL объявляется переменная, ее можно сделать разделяемой (shared). Все процессы, обращающиеся к библиотеке, для таких переменных будут использовать одно и то же место в физической памяти. (Здесь также важно не забыть о синхронизации.)
Протокол динамического обмена данными (Dynamic Data Exchange, DDE)
Этот протокол выполняет все основные функции для обмена данными между приложениями. Он очень широко использовался до тех пор, пока для этих целей не стали применять OLE (впоследствии ActiveX). На данный момент DDE используется достаточно редко, в основном для обратной совместимости.
Больше всего этот протокол подходит для задач, не требующих продолжительного взаимодействия с пользователем. Пользователю в некоторых случаях нужно только установить соединение между программами, а обмен данными происходит без его участия. Замечу, что все это в равной степени относится и к технологии OLE/ActiveX.
OLE/ActiveX
OLE (англ. Object Linking and Embedding)-- технология связывания и внедрения объектов в другие документы и объекты, разработанная корпорацией Майкрософт.
В 1996 году Microsoft переименовала технологию в ActiveX.
OLE позволяет передавать часть работы от одной программы редактирования к другой и возвращать результаты назад. Например, установленная на персональном компьютере издательская система может послать некий текст на обработку в текстовый редактор, либо некоторое изображение в редактор изображений с помощью OLE-технологии.
Основное преимущество использования OLE (кроме уменьшения размера файла) в том, что она позволяет создать главный файл, картотеку функций, к которой обращается программа. Этот файл может оперировать данными из исходной программы, которые после обработки возвращаются в исходный документ.
OLE используется при обработке составных документов, может быть использована при передаче данных между различными несвязанными между собой системами посредством интерфейса переноса, а также при выполнении операций с буфером обмена. Идея внедрения широко используется при работе с мультимедийным содержанием на веб-страницах, где используется передача изображения, звука, видео, анимации в страницах HTML либо в других файлах, также использующих текстовую разметку (например, XML и SGML). Однако, технология OLE использует архитектуру «толстого клиента», то есть сетевой ПК с избыточными вычислительными ресурсами. Это означает, что тип файла либо программа, которую пытаются внедрить, должна присутствовать на машине клиента. Например, если OLE оперирует таблицами Microsoft Excel, то программа Excel должна быть инсталлирована на машине пользователя.
В 1996 году Microsoft переименовала технологию OLE 2.0 в ActiveX. Были представлены элементы управления ActiveX, ActiveX документы и технология Active Scripting. Эта версия OLE в основном используется веб-дизайнерами для вставки в страницы мультимедийных данных.
Это действительно универсальная технология, и одно из многих ее применений - межпроцессный обмен данными. Хотя, стоит отметить, что OLE как раз для этой цели и создавалась (на смену DDE), и только потом была расширена. Специально для обмена данными существует интерфейс IDataObject. А для обмена данными по сети используется DCOM, которую под некоторым углом можно рассматривать как объединение ActiveX и RPC.
Каналы (pipes)
В среде операционной системы Microsoft Windows NT вам доступно такое удобное средство передачи данных между параллельно работающими процессами, как каналы типа Pipe. Это средство позволяет организовать передачу данных между локальными процессами, а также между процессами, запущенными на различных рабочих станциях в сети.
Каналы типа Pipe больше всего похожи на файлы, поэтому они достаточно просты в использовании.
Через канал можно передавать данные только между двумя процессами. Один из процессов создает канал, другой открывает его. После этого оба процесса могут передавать данные через канал в одну или обе стороны, используя для этого хорошо знакомые вам функции, предназначенные для работы с файлами, такие как ReadFile и WriteFile. Заметим, что приложения могут выполнять над каналами Pipe синхронные или асинхронные операции, аналогично тому, как это можно делать с файлами. В случае использования асинхронных операций необходимо отдельно побеспокоиться об организации синхронизации.
Каналы - это очень мощная технология обмена данными. Наверное, именно поэтому в полной мере они поддерживаются только в Windows NT/2000. В общем случае канал можно представить в виде трубы, соединяющей два процесса. Что попадает в трубу на одном конце, мгновенно появляется на другом. Чаще всего каналы используются для передачи непрерывного потока данных.
Именованные и анонимные каналы
Существуют две разновидности каналов Pipe - именованные (Named Pipes) и анонимные (Anonymous Pipes).
Как видно из названия, именованным каналам при создании присваивается имя, которое доступно для других процессов. Зная имя какой-либо рабочей станции в сети, процесс может получить доступ к каналу, созданному на этой рабочей станции.
Анонимные каналы обычно используются для организации передачи данных между родительскими и дочерними процессами, запущенными на одной рабочей станции или на “отдельно стоящем” компьютере.
Анонимные каналы используются достаточно редко, они просто передают поток вывода одного процесса на поток ввода другого. Именованные каналы передают произвольные данные и могут работать через сеть. (Именованные каналы поддерживаются только в WinNT/2000.)
Сокеты (sockets)
Сокеты (англ. socket -- разъём) -- название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет -- абстрактный объект, представляющий конечную точку соединения.
Следует различать клиентские и серверные сокеты. Клиентские сокеты грубо можно сравнить с оконечными аппаратами телефонной сети, а серверные -- с коммутаторами. Клиентское приложение (например, браузер) использует только клиентские сокеты, а серверное (например, веб-сервер, которому браузер посылает запросы) -- как клиентские, так и серверные сокеты.
Интерфейс сокетов впервые появился в BSD Unix. Программный интерфейс сокетов описан в стандарте POSIX.1 и в той или иной мере поддерживается всеми современными операционными системами.
приложение память межпроцессный сокет
Принципы сокетов
Каждый процесс может создать слушающий сокет (серверный сокет) и привязать его к какому-нибудь порту операционной системы (в UNIX непривилегированные процессы не могут использовать порты меньше 1024). Слушающий процесс обычно находится в цикле ожидания, то есть просыпается при появлении нового соединения. При этом сохраняется возможность проверить наличие соединений на данный момент, установить тайм-аут для операции и т.д.
Каждый сокет имеет свой адрес. ОС семейства UNIX могут поддерживать много типов адресов, но обязательными являются INET-адрес и UNIX-адрес. Если привязать сокет к UNIX-адресу, то будет создан специальный файл по заданному пути, через который смогут сообщаться любые локальные процессы путём чтения/записи из него (см. Доменный сокет Unix). Сокеты типа INET доступны из сети и требуют выделения номера порта.
Обычно клиент явно подсоединяется к слушателю, после чего любое чтение или запись через его файловый дескриптор будут передавать данные между ним и сервером.
Это очень важная технология, т.к. именно она отвечает за обмен данными в Интернет. Сокеты также часто используются в крупных ЛВС. Взаимодействие происходит через т.н. разъемы-"сокеты", которые представляют собой абстракцию конечных точек коммуникационной линии, соединяющей два приложения. С этими объектами программа и должна работать, например, ждать соединения, посылать данные и т.д. В Windows входит достаточно мощный API для работы с сокетами.
Почтовые слоты (mailslots)
Почтовые слоты - это механизм однонаправленного IPC. Если приложению известно имя слота, оно может помещать туда сообщения, а приложение-хозяин этого слота (приемник) может их оттуда извлекать и соответствующим образом обрабатывать. Основное преимущество этого способа - возможность передавать сообщения по локальной сети сразу нескольким компьютерам за одну операцию. Для этого приложения-приемники создают почтовые слоты с одним и тем же именем. Когда в дальнейшем какое-либо приложение помещает сообщение в этот слот, приложения-приемники получают его одновременно.
Объекты синхронизации
Как ни странно, объекты синхронизации тоже можно отнести к механизмам IPC. Конечно, объем передаваемых данных в данном случае очень невелик. Но именно эти объекты следует использовать, если одному процессу нужно передать другому что-то вроде "я закончил работу" или "я начинаю работать с общей памятью".
Microsoft Message Queue (MSMQ)
Расшифровывается это как Message Queuing (MSMQ) или Сервер очередей сообщений Microsoft. Очередь сообщений создана для взаимодействия приложений в распределенной среде (на разных компьютерах). Мы уже рассматривали подобные механизмы, например, socket или DCOM. Особенность MSMQ в том, что компьютеры не обязательно должны быть одновременно в сети. То есть можно отправить сообщение, можно получить, а за всем этим следит сервер MSMQ.
Есть несколько деталей, которые отличают очереди сообщений от других механизмов обмена данными в распределенной системе.
Доставка между клиентами одновременно не подключенными
Очередь сообщений поддерживается операционной системой
Очередь сообщений поддерживает транзакции
MSMQ 1.0 используется в Windows NT 4.0, Windows 95, and Windows 98.
MSMQ 2.0 используется в Microsoft® Windows® 2000.
Этот протокол действительно оправдывает свое название - он обеспечивает посылку сообщений между приложениями с помощью очереди сообщений. Основное его отличие от стандартной очереди сообщений Windows в том, что он может работать с удаленными процессами и даже с процессами, которые на данный момент недоступны (например, не запущены). Доставка сообщения по адресу гарантируется. Оно ставится в специальную очередь сообщений и находится там до тех пор, пока не появляется возможность его доставить.
Удаленный вызов процедур (Remote Procedure Call, RPC)
Строго говоря, это не совсем технология IPC, а скорее способ значительно расширить возможности других механизмов IPC. С помощью этой технологии общение через сеть становится совешенно прозрачным как для сервера, так и для клиента. Им обоим начинает казаться, что их "собеседник" расположен локально по отношению к ним.
Удалённый вызов процедур (или Вызов удалённых процедур) (от англ. Remote Procedure Call (RPC)) -- класс технологий, позволяющих компьютерным программам вызывать функции или процедуры в другом адресном пространстве (как правило, на удалённых компьютерах). Обычно, реализация RPC технологии включает в себя два компонента: сетевой протокол для обмена в режиме клиент-сервер и язык сериализации объектов (или структур, для необъектных RPC). Различные реализации RPC имеют очень отличающуюся друг от друга архитектуру и разнятся в своих возможностях: одни реализуют архитектуру SOA, другие CORBA или DCOM. На транспортном уровне RPC используют в основном протоколы TCP и UDP, однако, некоторые построены на основе HTTP (что нарушает архитектуру ISO/OSI, так как HTTP изначально не транспортный протокол).
Принцип
Идея вызова удалённых процедур (Remote Procedure Call -- RPC) состоит в расширении хорошо известного и понятного механизма передачи управления и данных внутри программы, выполняющейся на одной машине, на передачу управления и данных через сеть. Средства удалённого вызова процедур предназначены для облегчения организации распределённых вычислений и создания распределенных клиент-серверных информационных систем. Наибольшая эффективность использования RPC достигается в тех приложениях, в которых существует интерактивная связь между удалёнными компонентами с небольшим временем ответов и относительно малым количеством передаваемых данных. Такие приложения называются RPC-ориентированными.
Характерными чертами вызова удалённых процедур являются:
· Асимметричность, то есть одна из взаимодействующих сторон является инициатором;
· Синхронность, то есть выполнение вызывающей процедуры приостанавливается с момента выдачи запроса и возобновляется только после возврата из вызываемой процедуры.
Реализация удалённых вызовов существенно сложнее реализации вызовов локальных процедур. Можно обозначить следующие проблемы и задачи, которые необходимо решить при реализации RPC:
· Так как вызывающая и вызываемая процедуры выполняются на разных машинах, то они имеют разные адресные пространства, и это создает проблемы при передаче параметров и результатов, особенно если машины находятся под управлением различных операционных систем или имеют различную архитектуру (например, используется прямой или обратный порядок байтов). Так как RPC не может рассчитывать на разделяемую память, то это означает, что параметры RPC не должны содержать указателей на ячейки нестековой памяти и что значения параметров должны копироваться с одного компьютера на другой. Для копирования параметров процедуры и результата выполнения через сеть выполняется их сериализация.
· В отличие от локального вызова удалённый вызов процедур обязательно использует транспортный уровень сетевой архитектуры (например TCP), однако это остается скрытым от разработчика.
· Выполнение вызывающей программы и вызываемой локальной процедуры в одной машине реализуется в рамках единого процесса. Но в реализации RPC участвуют как минимум два процесса -- по одному в каждой машине. В случае, если один из них аварийно завершится, могут возникнуть следующие ситуации: при аварии вызывающей процедуры удалённо вызванные процедуры станут «осиротевшими», а при аварийном завершении удалённых процедур станут «обездоленными родителями» вызывающие процедуры, которые будут безрезультатно ожидать ответа от удалённых процедур.
· Существует ряд проблем, связанных с неоднородностью языков программирования и операционных сред: структуры данных и структуры вызова процедур, поддерживаемые в каком-либо одном языке программирования, не поддерживаются точно так же во всех других языках. Таким образом имеется проблема совместимости, до сих пор не решённая ни с помощью введения одного общепринятого стандарта, ни с помощью реализации нескольких конкурирующих стандартов на всех архитектурах и во всех языках.
Иногда при запуске какой-либо программы появляется сообщение, что не найден файл *.dll
. Для операционных систем Microsoft Windows, большая часть функциональных возможностей операционной системы обеспечивается библиотеками динамической компоновки (DLL). Кроме того, некоторые возможности программ могут быть реализованы в библиотеках DLL. Например некоторые программы могут содержать много различных модулей и при работе использовать только часть из них. Таким образом операционная система и программы загружаются быстрее, работают быстрее и занимают меньше места на диске компьютера.
Что такое DLL?
DLL — это библиотека, содержащая код и данные, которые могут использоваться несколько программами одновременно. Например, в операционных системах Windows, библиотека Comdlg32.dll
выполняет общие функции, связанные с диалоговыми окнами. Таким образом каждая программа может использовать функцию, которая содержится в этой библиотеке для реализации диалогового окна Открыть. Это позволяет повысить уровень повторного использования кода и эффективного использования памяти.
С помощью библиотек можно реализовать модульность для программы, в виде отдельных компонентов. Например бухгалтерскую программу можно продать по модулям. Каждый модуль может быть загружен в основной программе во время выполнения установки. Отдельные модули загружается только при запросе функций заложенных в них, поэтому загрузка программы выполняется быстрее.
Кроме того обновления легче применить для каждого модуля, не влияя на другие части программы. Например имеется программа по зарплате и надо изменить налоговые ставки за каждый год. Когда эти изменения изолированы в библиотеке, можно применить обновления без необходимости построения или установки программы целиком. Давайте рассмотрим пример создания библиотеки с самыми простыми математическими методами, такие как произведение, деление, сумма и разность.
Для начала, создадим новый проект, для этого запустите Microsoft Visual Studio
и перейдите в меню Файл -> Создать -> Проект…
или выполните сочетание клавиш Ctrl+Shift+N
.
В правой части программы у вас откроется вкладка «Окно классов» .
Выберете по умолчанию созданный класс Class1, сделайте клик правой клавишей мыши по нему и выберете «Переименовать… ».
Добавим в класс Calculator несколько методов и добавим к ним описание.
namespace ClassLibrary1 { /// /// Математический класс /// public class Calculator { /// /// Метод возвращает сумму двух целых чисел /// /// /// /// public static int Summ(int firstNumber, int secondNumber) { return firstNumber + secondNumber; } /// /// Метод возвращает разность двух целых чисел /// /// /// /// public static int Division(int firstNumber, int secondNumber) { return firstNumber - secondNumber; } /// /// Метод возвращает произведение двух чисел /// /// /// /// public static long Multiply(long x, long y) { return (x * y); } /// /// Метод возвращает деление двух чисел /// /// /// /// public static int Residual(int firstNumber, int secondNumber) { return (firstNumber / secondNumber); } } } По умолчанию для всех проектов стоит режим построения Debug (режим отладки), переведем проект в режим построения конечной версии (Release ). Для этого перейдите в обозреватель решений и сделав клик правой клавишей мыши по названию проекта, выберете в открывшемся контекстном меню пункт «Свойства ».