Разработка программы для автоматизации работы кардиоцентра

Курсовой проект - Компьютеры, программирование

Другие курсовые по предмету Компьютеры, программирование

ае приложение прекращает свою работу (никакие ловушки исключений не помогают) и, если оно выполняется под Windows 95 или Windows 98, чаще всего требуется перезагрузка операционной системы. Понятно, что возврат указателя стека в первоначальное состояние должен происходить по окончании работы метода. Но при этом существуют две возможности - возврат указателя на место может производить как вызываемый метод по окончании работы, так и вызывающий метод после завершения работы вызываемого метода. В принципе, в различных языках программирования реализуются обе указанные возможности - очищать стек могут и вызванный, и вызывающий методы. Поскольку модуль пишется на одном языке программирования, то эти проблемы скрыты от программиста: очистка стека производится по специфичному для данного языка протоколу. Но если используются различные модули, код для которых реализован на различных языках программирования, то возникают проблемы. Например, в C++ стек очищается в методе, который вызвал второй метод, после окончания его работы. В Delphi же стек очищается в том же самом методе, где он используется, перед окончанием его работы. Если *.exe-модуль, созданный на языке C++, вызывает метод из DLL, созданный на Delphi, то перед окончанием работы метода в DLL стек будет очищен. После этого управление передается модулю, реализованном на C++, который также попытается очистить стек, - такое действие приведет к краху стека.

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

 

procedure DoSomething(N:integer; D:TDateTime);

Указанный способ заключается в том, что сначала в стек может быть помещена константа N, а затем D (слева направо) или вначале помещается константа D, а затем N (справа налево). Кроме того, некоторые языки программирования (в частности, Delphi) часть параметров метода вообще не помещают в стек, а передают их через регистры процессора. К тому же в разных языках программирования параметры могут помещаться в стек как слева направо, так и справа налево. Если они были помещены слева направо, а вызываемый метод будет читать справа налево, то получится путаница: в качестве значения константы N вызываемый метод будет считать значение правой половины константы D, а константу D он будет формировать из константы N и левой половины D.

По этой причине в любом языке программирования предусмотрена возможность объявить, какой из методов - вызываемый или вызывающий, будет очищать стек и в какой последовательности параметры метода помещаются в стек. Такое объявление называется соглашением вызова (calling conversion).

Для методов, экспонируемых в DLL, рекомендуется (но не обязательно) использовать то же соглашение вызова, что и в Windows API. Для 32-разрядных приложений Windows API методы реализованы таким образом, что параметры помещаются в стек справа налево и стек очищает вызываемый метод; при этом не используются регистры процессора для передачи данных. Этим условиям удовлетворяет директива stdcall. Если после заголовка метода отсутствует соглашение о вызове, то по умолчанию Delphi использует соглашение register.

Второе служебное слово в заголовке метода - export - информирует компилятор о том, что код для данного метода должен быть создан таким образом, чтобы его можно было вызывать из других модулей. Эта директива требуется при реализации DLL в Delphi 3; в Delphi 4 и 5 ее можно опустить.

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

Для экспонирования метода в секции exports просто приводится его название, после которого следует либо служебное слово index с целочисленным идентификатором после него (идентификатор должен быть больше нуля), либо служебное слово name с текстовым идентификатором, либо оба вместе. Внешний модуль может обращаться к конкретному методу как по индексу, так и по имени.

 

Статическая и динамическая загрузка DLL

 

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

При статической загрузке для вызова другого модуля следует в какой-либо из секций описать метод из DLL следующим образом:

 

function Add1(K:integer):integer; stdcall; external FirstLib.dll name CalculateSum;

или

function Add1(K:integer):integer; stdcall; external FirstLib.dll index 1;

 

При таком определении метода DLL будет загружена немедленно после старта приложения и выгружена вместе с его завершением. В приведенном выше примере следует обратить внимание на то, что после имени динамической библиотеки указано ее расширение (FirstLib.dll). Такая конструкция необходима для загрузки библиотеки в Windows NT, поскольку без расширения *.dll файл не будет найден! В Windows 95 расширение не обязательно.

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