Глава 11. Динамически компонуемые библиотеки.

 

             Динамически компонуемые  библиотеки (DLL) позволяют несколь-

        ким прикладным программа Windows или DOS защищенного режима  сов-

        местно использовать код и ресурсы. В Borland Pascal вы можете как

        использовать существующие DLL,  так и написать  свои  собственные

        DLL, которые можно применять в других программах.

Что такое DLL?

             DLL - это выполняемый модуль, содержащий программный код или

        ресурсы, используемые  другими  прикладными  программами или DLL.

        Концептуально динамически компонуемая библиотека аналогичная  мо-

        дулю - они обеспечивают для программ процедуры и функции.  Однако

        между DLL и модулями имеются существенные различия.  В частности,

        модули компонуются статически, а DLL - динамически.

 

             Когда программа  использует процедуру или функцию из модуля,

        копия кода этой процедуры или функции  статически  компонуется  с

        выполняемым файлом программы.  Если две программы выполняются од-

        новременно и используют одну и ту же процедуру и функцию  модуля,

        то в  системе  будет  присутствовать две копии этой подпрограммы.

        Эффективнее было бы использовать одну  копию.  Такую  возможность

        предоставляет DLL.

 

             В отличие  от  модуля  DLL не компонуется с использующей DLL

        программой. Вместо этого код и ресурсы DLL находятся в  отдельном

        выполняемом файле  с расширением .DLL.  Этот файл должен присутс-

        твовать при выполнении программы-клиента.  Вызываемые  программой

        процедуры и  функции  динамически  компонуются  со своими точками

        входа в используемой программе DLL.

 

             Другое отличие модулей от DLL состоит в том,  что модули мо-

        гут экспортировать  типы,  константы,  данные и объекты,  а DLL -

        только процедуры и функции.

 

             Чтобы ее можно было использовать в программе Borland Pascal,

        DLL не обязательно должна быть написана на Borland Pascal.  Кроме

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

        DLL, написанные на Borland Pascal.  DLL,  таким образом, идеально

        подходит при программных проектах, реализуемых на нескольких язы-

        ках.

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

             Чтобы модуль мог использовать процедуру или функцию  в  DLL,

        он должен  импортировать процедуру или функцию с помощью описания

        external. Например,  в следующем описании из DLL и именем  KERNEL

        (ядро Windows) импортируется функция с именем GlobalAlloc:

             function GlobalAlloc(Glags: Word; Bytes: Longint): THandle;

                 far; external 'KERNEL' index 15;

 

             В импортируемой процедуре или функции директива external за-

        нимает место описательной и операторной части, которые нужно было

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

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

        ключевым словом far или директивой компилятора {$F+}; во всем ос-

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

 

             Borland Pascal  импортирует процедуры и функции тремя спосо-

        бами:

 

             - по имени;

             - по новому имени;

             - по порядковому номеру.

 

             Формат директив external для каждого из трех методов показан

        в приведенном ниже примере.

 

             Когда оператор index или name не указан, процедура или функ-

        ция экспортируются по имени.  Это имя совпадает с идентификатором

        процедуры или  функции.  В  данном примере процедура ImportByName

        импортируется из библиотеки 'TESTLIB' по имени 'IMPORTBYNAME':

 

             procedure ImportByName; external 'TESTLIB';

 

             Когда задан оператор name,  процедура или функция импортиру-

        ется  под именем,  отличным от имени идентификатора.  В следующем

        примере процедура  ImportByName   импортируется   из   библиотеки

        'TESTLIB' по имени 'REALNAME':

 

             procedure ImportByName; external 'TESTLIB'name 'REALNAME'

 

             Наконец, при  наличии  оператор  index процедура или функция

        импортируется по порядковому значению. Такой вид импорта уменьша-

        ет время  загрузки модуля,  так как отпадает необходимость поиска

        имени  в  таблице  имен  DLL.  В  следующем   примере   процедура

        ImportByOrd импортируется из библиотеки 'TESTLIB':

 

             procedure ImportByOrd; external 'TESTLIB' index 5;

 

             Имя DLL  задается  после  ключевого слова external,  а новое

        имя, заданное в операторе name,  не  обязано  представлять  собой

        строковые литералы.  Допускается  любое строковое выражение-конс-

        танта. Аналогично,  порядковый  номер,  задаваемый  в   операторе

        index, может быть любым целочисленным выражением-константой.

 

             const

                TestLib = TestLib;

                Ordinal = 5;

             procedure ImportByName; external TestLib;

             procedure ImportByName; external TestLibname 'REALNAME'

             procedure ImportByOrd; external TestLib index Ordinal;

 

             Хотя DLL может содержать переменные, импортировать их в дру-

        гие модули невозможно.  Любой доступ к переменным DLL должен осу-

        ществляться через процедурный интерфейс.

Модули импорта

             Описания импортируемых процедур и функций  могут  помещаться

        непосредственно в программу, которая их импортирует. Однако обыч-

        но они объединяются в модуль импорта,  содержащий  описания  всех

        процедур и функций в DLL, а также все типы и константы, необходи-

        мые для интерфейса с DLL. Примерами таких модулей импорта являют-

        ся поставляемые  с  Borland  Pascal  модули WinTypes,  WinProcs и

        WinAPI. Модули импорта не обязательны для интерфейса  с  DLL,  но

        они значительно  упрощают обслуживание использующих множество DLL

        проектов.

 

             В качестве примера рассмотрим DLL с именем DATETIME.DLL, со-

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

        времени с помощью типа записи, содержащей число, месяц, год и за-

        писи, которая содержит секунду, минуту и час. Вместо спецификации

        соответствующих описаний процедуры,  функции и типа в каждой  ис-

        пользующей DLL  программе вы можете построить наряду с DLL модуль

        импорта. В следующем примере создается файл .TPW    предположе-

        нии, что целевой платформой является Windows), но отсутствуют код

        и данные для использующей его программы.

 

             unit DateTime;

 

             interface

 

             type

               TTimeRec = record

                   Second: Integer;

                   Minute: Integer;

                   Hour: Integer;

               end;

 

             type

               TDateRec

                  TDateRec = record

                    Day: Integer;

                    Month: Integer;

                    Year: Integer;

                  end;

 

             procedure SetTime(var Time: TTimeRec);

             procedure GetTime(var Time: TTimeRec);

             procedure SetDate(var Date: TDateRec);

             procedure GetDate(var Date: TDateRec);

 

             inplementation

 

             procedure SetTime; external 'DATETIME' index 1;

             procedure GetTime; external 'DATETIME' index 2;

             procedure SetDate; external 'DATETIME' index 3;

             procedure GetTime; external 'DATETIME' index 4;

 

             end.

 

             Любая программа,   использующая  DATETIME.DLL  может  теперь

        просто задать в своем операторе uses  модуль  DateTime.  Приведем

        пример программы Windows:

 

             program ShowTime;

 

             uses WinCrt, DateTime;

 

             var

               Time: TTimeRec;

 

             begin

               GetTime(Time);

               with Time do

                 WriteLn('Текущее время: ', Hour, ':', Minute, ':',

                    Second);

             end.

 

             Другим преимуществом  использования  модуля импорта,  такого

        как DateTime, является то, что при модификации DATETIME.DLL обно-

        вить требуется только модуль импорта DateTime.

 

             Когда вы компилируете использующую DLL программу, компилятор

        не ищет DLL,  так что ее присутствие  не  требуется.  Однако  DLL

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

 

             Если вы пишете собственные DLL, они не компилируются автома-

        тически при компиляции использующей ее программы с помощью коман-

        ды CompileіMake. DLL следует компилировать отдельно.

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

             Директива external обеспечивает возможность статического им-

        порта процедур и функций из DLL.  Статически импортируемая проце-

        дура и функция всегда ссылается на одну и ту  же  точку  входа  в

        DLL. Расширения  Windows и защищенного режима DOS Borland поддер-

        живает также динамический импорт,  при котором имя DLL и имя  или

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

        время выполнения.  Приведенная ниже программа ShowTime использует

        динамический импорт  для вызова процедуры GetTime в DATETIME.DLL.

        Обратите внимание на использование переменной  процедурного  типа

        для представления адреса процедуры GetTime.

 

             program ShowTime;

 

             uses WinProcs, WinTypes, WinCrt;

 

             type

               TTimeRec = record

                   Second: Integer;

                   Minute: Integer;

                   Hour: Integer;

               end;

               TGetTime = procedure(var Time: TTimeRec);

 

             var

               Time: TTimeRec;

               Handle: THAndle;

               GetTime: TGetTime;

 

             begin

               Handle := LoadLibrary('DATETIME.DLL');

               if Handle >= 32 then

               begin

                 @GetTie := GetProcAddress(Handle, 'GETTIME');

                 if @GetTime <> nil then

                  begin

                    GetTime(Time);

                    with Time do

                      WriteLn('Текущее время: ', Hour, ':', Minute, ':',

                               Second);

                   end;

                   FreeLibrary(Handle);

                 end;

             end;

Написание DLL

             Структура DLL Borland Pascal идентичная структуре программы,

        но DLL начинается вместо заголовка program с  заголовка  program.

        Заголовок library указывает Borland Pascal, что нужно создать вы-

        полняемый файл с расширением .DLL, а не с расширением .EXE, и вы-

        полняемый файл помечается как DLL.

 

         библиотека

         і

         і   ЪДДДДДДДДДДДДДї   ЪДДДї                    ЪДДДДДДї

         АДД>і  заголовок  ГДД>і ; ГДВДДДДДДДДДДДДДДДДДДі блок ГДДДДДДД>

             і библиотеки  і   АДДДЩ і   ЪДДДДДДДДДДї ^ АДДДДДДЩ

             АДДДДДДДДДДДДДЩ         АДД>і оператор ГДЩ

                                         і   uses   і

                                         АДДДДДДДДДДЩ

 

                        ЪДДДДДДДДДї   ЪДДДДДДДДДДДДДДДї

         заголовок ДДДД>і library ГДД>і идентификатор ГДДДДД>

         процедуры      АДДДДДДДДДЩ   АДДДДДДДДДДДДДДДЩ

 

             В приведенном ниже примере приведена  очень  простую  DLL  с

        двумя экспортируемыми функциями Min и Max, которые вычисляют наи-

        меньшее и наибольшее из двух целочисленных значений.

 

             library MinMax;

 

             function Min(X, Y: Integer): Integer; export;

             begin

               if X < Y then Min := X else Min := Y;

             end;

 

             function Max(X, Y: Integer): Integer; export;

             begin

               if X > Y then Max := X else Max := Y;

             end;

 

             exports

                Min index 1,

                Max index 2;

 

             begin

             end.

 

             Обратите внимание на использование для подготовки Min и Max,

        для экспорта ключевого слова export,  и на оператор exports,  ис-

        пользуемый для фактического экспорта двух подпрограмм,  указываю-

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

 

            Хотя предыдущий пример этого не показывает,  библиотека может

        состоять из  нескольких  модулей.  В  таких случаях исходный файл

        библиотеки часто сводится к оператору uses,  оператору exports  и

        коду инициализации библиотеки. Например:

 

             library Eritors;

 

             uses EdInit, EdInOut, EdFormat, EdPrint;

 

             exports

               InitEditors index 1,

               DoneEditors index 2,

               InsertText index 3,

               DeleteSelection index 4,

               FormatSelection index 5,

               PrintSelection index 6,

                    .

                    .

                    .

               SetErrorHandler index 53;

 

             begin

               InitLibrary;

             end.

Директива процедуры export

             Если процедуры и функции должны  экспортироваться  DLL,  они

        должны компилироваться с директивой компилятора export. Директива

        export принадлежит к тому же семейству процедурных директив,  что

        и near,  far,  inline  и interrupt.  Это означает,  что директива

        export, если она присутствует,  должна указываться  перед  первым

        заданием процедуры или функции - она не может указываться в опре-

        деляющем описании или в опережающем описании.

 

             Директива export делает процедуру или  функцию  экспортируе-

        мой. Она  принудительно  использует  для подпрограммы дальний тип

        вызова и подготавливает ее для экспорта,  генерируя для процедуры

        специальный код входа и выхода.  Заметим, однако, что фактический

        экспорт процедуры или функции не происходит, пока подпрограмма не

        перечисляется в операторе exports библиотеки.

Оператор exports

             Процедура или функция экспортируется DLL, когда она указыва-

        ется в операторе exports библиотеки.

 

         оператор exports

         і   ЪДДДДДДДДДї   ЪДДДДДДДДДДДДДДДДї            ЪДДДї

         АДД>і exports ГДД>і список экспортаГДДДДДДДДДДД>і ; ГДДДДДДД>

             АДДДДДДДДДЩ   АДДДДДДДДДДДДДДДДЩ            АДДДЩ

 

                              ЪДДДДДДДДДДДДДДДДї

         список экспорта ДДВД>і запись экcпортаГДДДДДДДДДДД>

                           і  АДДДДДДДДДДДДДДДДЩ  ^

                           і        ЪДДДї         і

                           АДДДДДДД>і ; ГДДДДДДДДДЩ

                                    АДДДЩ

 

         оператор exports

           і    ЪДДДДДДДДДДДДДДДї

           АДДД>і идентификатор ГДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї

                АДДДДДДДДДДДДДДДЩ  і   ЪДДДДДДДї  ЪДДДДДДДДДДДДДДДДДї ^ і

                                   АДД>і index ГД>і целая константа ГДЩ і

                                       АДДДДДДДЩ  АДДДДДДДДДДДДДДДДДЩ   і

         ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

         АДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДД>

           і ЪДДДДДДї   ЪДДДДДДДДДДДДДДДДДДДДДї ^і  ЪДДДДДДДДДДї  ^

           А>і name ГДД>і строковая константа ГДЩАД>і resident ГДДЩ

             АДДДДДДЩ   АДДДДДДДДДДДДДДДДДДДДДЩ     АДДДДДДДДДДЩ

 

             Оператор exports  может встречаться в любом месте описатель-

        ной части программы или библиотеки и любое число раз.  Каждая за-

        пись в операторе exports задает идентификатор экспортируемой про-

        цедуры или функции. Однако, эта процедура или функция должна опи-

        сываться до оператора exports, и ее описание должно содержать ди-

        рективу export.  Перед идентификатором в операторе exports вы мо-

        жете указать  идентификатор модуля с точкой;  это называется пол-

        ностью уточненным идентификатором.

 

             Запись экспорта может также включать в себя оператор  index,

        который состоит из ключевого слова index,  за которым следует це-

        лочисленное значение в диапазоне от 1 до  32767.  Когда  задается

        оператор index,  для  экспортируемой процедуры или функции должно

        использоваться специальное порядковое  значение.  Если  в  записи

        экспорта оператор index отсутствует, то порядковое значение прис-

        ваивается автоматически.

 

             Запись может содержать оператор name, состоящий из ключевого

        слова name,  за которым следует строковая константа.  При наличии

        оператора name экспортируемая процедура или функция  должна  экс-

        портироваться с  помощью  задаваемого строковой константой имени.

        Если оператор name в записи экспорта  отсутствует,  то  процедура

        или функция экспортируется по ее идентификатору (символы которого

        преобразуются в верхний регистр).

 

             Наконец, запись экспорта может включать в себя ключевое сло-

        во resident.  При  задании ключевого слова resident информация об

        экспорте остается в памяти, пока DLL загружена. Параметр resident

        существенно уменьшает время поиска подпрограммы в DLL по имени.

 

             Программа может содержать оператор exports,  но это встреча-

        ется редко,  так как Windows не позволяет  прикладным  программам

        экспортировать функции,  используемые другие прикладными програм-

        мами.

Код инициализации библиотеки

             Операторная часть  библиотеки  состоит из кода инициализации

        библиотеки. Код  инициализации  выполняется  только  один раз при

        первоначальной загрузке библиотеки. Когда другие прикладные прог-

        раммы будут использовать уже загруженную библиотеку,  код инициа-

        лизации повторно не выполняется, но увеличивается счетчик исполь-

        зования DLL.

 

             DLL хранится в памяти,  пока ее счетчик использования больше

        нуля. Когда  счетчик использования становится нулевым,  указывая,

        что все использующие DLL прикладные программы  завершили  работу,

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

        да. Процедуры  выхода   регистрируются   с   помощью   переменной

        ExitProc, которая описывается в Главе 22 "Вопросы управления".

 

             Код инициализации  DLL обычно выполняет такие задачи как ре-

        гистрация класса окна для содержащихся в DLL оконных  процедур  и

        установка начальных значений для глобальных переменных DLL. Уста-

        новив в нулевое значение переменную ExitCode,  код  инициализации

        библиотеки может указать состояние ошибки (ExitCode описывается в

        модуле System).  По умолчанию ExitCode равна 1,  что указывает на

        успешную инициализацию. Если код инициализации устанавливает зна-

        чение этой переменной в 0,  то DLL выгружается из системной памя-

        ти,  и вызывающая прикладная программа уведомляется  о  неудачной

        загрузке DLL.

 

             Когда выполняется библиотечная процедура выхода,  переменная

        ExitCode не  содержит  код  завершения  процесса.  Вместо   этого

        ExitCode содержит  одно  из значений wep_System или wep_Free_DLL,

        определенных в модуле WinTypes.  wep_System указывает на заверше-

        ние работы Windows, а wep_Free_DLL указывает на то, что выгружена

        данная DLL.

 

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

        рой выхода:

 

             library Test;

 

             {$S-}

 

             uses WinTypes, WinProcs;

 

             var

               SaveExit: Pointer;

 

             procedure LibExit; far;

             begin

                if ExitCode = wep_System_Exit then

                    begin

                    { выполняется завершение работы системы }

                      .

                      .

                      .

                   end else

                   begin

                     .

                     .

                     .

                   { разгружается DLL }

                     .

                     .

                     .

                   end;

                   ExitProcess : SaveExit;

             end;

 

             begin

               .

               .

               .

             { выполнить инициализацию DLL }

               .

               .

               .

             SaveExit := ExitProc;   { сохранить старый указатель

                                       процедуры выхода }

             ExitProc := @LibExit;   { установка процедуры выхода

                                       LibExit }

             end.

 

             В защищенном режиме DOS передаваемое  процедуре  выхода  DLL

        значение ExitCode всегда равно 0 и соответствует wep_FREE_DLL.

 

             После разгрузки DLL экспортируемая функция вызывает процеду-

        ру WEP (процедура выхода Windows)  DLL,  если  она  присутствует.

        Библиотека Borland Pascal автоматически экспортирует функцию WEP,

        которая продолжает вызывать записанный в переменной ExitProc  ад-

        рес,  пока ExitProc не примет значения nil.  Поскольку этот меха-

        низм процедур выхода соответствует работе с процедурами выхода  в

        программах Borland Pascal, и в программах, и в библиотеках вы мо-

        жете использовать одну и ту же логику процедур выхода.

 

             Поскольку операционная система при завершении DLL переключа-

        ет внутренний стек, процедуры выхода в DLL должны компилироваться

        с запрещением проверки стека (в состоянии {$S-}). Кроме того, ес-

        ли в  процедуре  выхода  DLL  происходит ошибка этапа выполнения,

        операционная система аварийно завершает работу,  поэтому  вы  для

        предотвращения  ошибок этапа выполнения вы должны включить в свой

        код достаточное количество проверок.

Замечания по программированию библиотек

             В следующих разделах описаны некоторые важные моменты, кото-

        рые следует иметь в виду при работе с DLL.

Глобальные переменные в DLL

             DLL имеет свой собственный сегмент данных, и любая описанная

        в DLL переменная является локальной для этой DLL.  DLL  не  может

        получить доступ к переменным, описанным в вызывающих DLL модулях,

        и не может экспортировать переменные в другие модули.  Такой дос-

        туп должен реализовываться через процедурный интерфейс.

Глобальные переменные и файлы в DLL

             Как правило,  DLL не является "владельцем" каких-либо откры-

        ваемых ей  файлов  или получаемых ей от системы глобальных блоков

        памяти. Такими объектами владеет (прямо или косвенно) сама  прик-

        ладная программа, вызывающая DLL.

 

             Когда прикладная программа завершает работу,  любые открытые

        файлы, владельцем которых она является,  автоматически закрывают-

        ся, а  все принадлежащие ей глобальные блоки памяти автоматически

        освобождаются. Это означает, что описатели данных файлов и блоков

        памяти, записанные  в DLL в глобальных переменных,  могут в любое

        время стать недопустимыми без уведомления DLL.  По  этой  причине

        DLL не  следует полагаться на допустимость описателя файла и гло-

        бальных описателей памяти,  хранящихся между обращениями к DLL  в

        глобальных переменных. Такие описатели следует сделать параметра-

        ми процедур и функций  DLL,  и  вызывающая  прикладная  программа

        должна отвечать за их поддержку.

 

             В Windows глобальные блоки памяти,  распределенные с атрибу-

        том gmem_DDEShare (определенные в модуле  WinTypes),  принадлежат

        DLL, а  не  вызывающим прикладным программам.  Такие блоки памяти

        остаются распределенными, пока они явно не освобождаются DLL, или

        пока DLL не выгружается.

 

             Администратор памяти  защищенного режима DOS не поддерживает

        совместно используемых   блоков   памяти   и   игнорирует    флаг

        gmem_DDEShare. В  защищенном  режиме DOS распределяемые DLL блоки

        памяти всегда принадлежат вызывающей библиотеку DLL программе.

DLL и модуль System

             В продолжении  существования DLL переменная HInstance содер-

        жит описатель экземпляра DLL.  Переменные FPrevInst и  CmdShow  в

        DLL всегда равны 0 (как и переменная PrefixSeg), поскольку DLL не

        имеет префикса программного сегмента (PSP). В прикладной програм-

        ме PrefixSeg никогда не равна 0,  поэтому проверка PrefixSeg <> 0

        возвращает True,  если текущем модулем является прикладная  прог-

        рамма, и False, если текущим модулем является DLL.

 

             Чтобы обеспечить правильную работу администратора динамичес-

        ки распределяемой области, содержащегося в модуле System, код за-

        пуска библиотеки устанавливает переменную HeapAllocFlags в значе-

        ние gmem_Moveable + gmem_DDEShare. В Windows это приводит к тому,

        что все  блоки  памяти,  распределенные  через  процедуры  New  и

        GetMem, будут принадлежать DLL,  а не  вызывающей  ее  прикладной

        программе.

 

                   Примечание: Подробности  об  администраторе  памяти вы

              можете найти в Главе 21.

Ошибки этапа выполнения в DLL

             Если в  DLL  происходит ошибка этапа выполнения,  вызывающая

        DLL прикладная программа завершает работу.  При этом сама DLL  не

        обязательно удаляется  из  памяти,  поскольку она может использо-

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

 

             Поскольку DLL не может знать,  вызывается ли она из приклад-

        ной программы  Borland Pascal или из прикладной программы,  напи-

        санной на другом языке программирования, то DLL не может вызывать

        процедуры выхода  прикладной  программы  до завершения прикладной

        программы. Прикладная программа просто прерывается и  выгружается

        из памяти.  По  этой причине,  чтобы таких ошибок не происходило,

        нужно обеспечить в DLL достаточное количество проверок.

 

             Если в DLL под Windows происходит ошибка  этапа  выполнения,

        то надежнее всего полностью выйти в Windows. Если вы просто пыта-

        етесь модифицировать и перестроить сбойный код DLL, а затем снова

        выполнить прикладную программу,  Windows не будет загружать новую

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

        Windows  и перезапустите ее,  а Borland Pascal обеспечит загрузку

        корректной версии DLL.

DLL и сегменты стека

             В отличие  от прикладной программы DLL не имеет своего собс-

        твенного сегмента стека. Вместо этого она использует сегмент сте-

        ка вызывающей DLL прикладной программы. Это может создать пробле-

        мы в подпрограмме DLL,  которые полагают,  что регистры DS  и  SS

        ссылаются на один и тот же сегмент,  как это имеет место в модуле

        прикладной программы Windows.

 

             Borland Pascal никогда не  генерирует  код,  подразумевающий

        равенство DS  =  SS,  и  в библиотеке исполняющей системы Borland

        Pascal таких предположений не делается.  Если вы  пишете  код  на

        языке ассемблера,  то не полагайтесь на то,  что регистры DS и SS

        содержат одно и то же значение.

Создание совместно используемых DLL

             Borland Pascal поддерживает DLL, которые могут совместно ис-

        пользоваться в защищенном режиме DOS и в Windows.  Совместно  ис-

        пользуемые DLL совместимы на уровне двоичного кода. Это означает,

        что один и тот же файл .DLL  может  использоваться  в  прикладной

        программе защищенного  режима  DOS  или  в  прикладной  программе

        Windows.

 

             При компиляции совместно используемой DLL в качестве целевой

        платформы нужно выбирать Windows:

 

             * В  IDE выберите команду CompileіTarget и в диалоговом окне

               Target (Целевая платформа) укажите Windows.

 

             * При использовании компилятора,  работающего в  режиме  ко-

               мандной строки,  для  выбора  в качестве целевой платформы

               Windows используйте переключатель /CW.

 

             DLL, скомпилированная  для  защищенного  режима   DOS,   под

        Windows использоваться  не может,  так как библиотека исполняющей

        системы защищенного режима DOS использует отдельные  функциональ-

        ные вызовы DOS и DPMI, которые следует избегать в Windows.

 

             Совместно используемая  DLL может взаимодействовать с опера-

        ционной системой (DOS защищенного режиме или Windows) только  че-

        рез модуль  WinAPI.  Этот модуль представляет функции,  общие для

        защищенного режима DOS  и  Windows.  Другие  интерфейсные  модули

        Windows, такие  как WinTypes и WinProcs,  описывают большое число

        подпрограмм API, не поддерживаемых в защищенном режиме DOS.

 

                   Примечание: О модуле WinAPI рассказывается в Главе  17

              "Программирование в защищенном режиме DOS".

 

             Важно отметить,  что  хотя  совместно используемая DLL может

        выполняться одновременно и под Windows, в окне защищенного режима

        Windows DOS,  связь  через  DLL между двумя операционными средами

        невозможна. Реально в системе будет присутствовать две копии DLL,

        каждая из  которых защищена от другой и использует полностью изо-

        лированную область памяти.