Автор работы: Пользователь скрыл имя, 24 Мая 2013 в 21:12, дипломная работа
Найчастіше проект підключається до DLL статично, або неявно, на етапі компонування. Завантаженням DLL при виконанні програми управляє операційна система. Однак, DLL можна завантажити і явно, або динамічно, в ході роботи додатку.
Мета дипломної роботи розробити самостійно декілька власних DLL бібліотек у середовищі Delphi7, та показати приклади роботи з ними.
Вступ 4
1.Огляд відомих рішень 6
2. Вибір метода рішення 11
2.1 Постановка задачі 11
2.2 Обраний метод 11
3. Реалізація 21
4.Охорона праці 26
4.1 Характеристика приміщення 26
4.2 Дослідження природного освітлення 29
4.3 Дослідження штучного освітлення 33
4.4 Дослідження достатності вентиляції 35
4.5 Дослідження пожежобезпеки 38
Список Літератури 40
Крім цих можливостей, існує й інший спосіб, а саме: послідовність занесення в стек параметрів методу. Припустимо, є метод, який використовує для роботи два параметри:
procedure DoSomething(N:integer; D:TDateTime); |
Зазначений спосіб полягає в тому, що спочатку в стек може бути поміщена константа N, а потім D (зліва направо) або спочатку поміщається константа D, а потім N (справа наліво). Крім того, деякі мови програмування (зокрема, Delphi) частину параметрів методу взагалі не поміщають в стек, а передають їх через регістри процесора. До того ж у різних мовах програмування параметри можуть міститися в стек як зліва направо, так і справа наліво. Якщо вони були поміщені зліва направо, а викликуваний метод буде читати справа наліво, то вийде плутанина: як значення константи N викликуваний метод буде вважати значення правої половини константи D, а константу D він буде формувати з константи N і лівої половини D.
З цієї причини в будь-якій мові програмування передбачена можливість оголосити, який з методів - викликуваний або викликаючий, буде очищати стек і в якій послідовності параметри методу поміщаються в стек. Таке оголошення називається угодою виклику (calling conversion); є ряд зарезервованих слів, які поміщаються після заголовка методів, як показано в таблиці.
Директива |
Порядок слідування параметрів |
Очистка стека |
Використанна регістрів |
register |
Зліва направо |
викликуваний метод |
+ |
pascal |
Зліва направо |
викликуваний метод |
- |
cdecl |
Зліва направо |
викликуваний метод |
- |
stdcall |
Зліва направо |
викликуваний метод |
- |
safecall |
Зліва направо |
викликуваний метод |
- |
Для методів, що експонуються в DLL, рекомендується (але не обов'язково) використовувати угоду виклику, що і в Windows API. Для 32-розрядних додатків Windows API методи реалізовані таким чином, що параметри поміщаються в стек справа наліво і стек очищає викликуваний метод; при цьому не використовуються регістри процесора для передачі даних. Цим умовам задовольняє директива stdcall, яка в описаному вище прикладі поміщається після заголовка методу AddOne. Якщо після заголовка методу відсутня угода про виклик, то за замовчуванням Delphi використовує угоду register.
Однак написаного вище коду ще недостатньо для виклику методу AddOne з іншого модуля. Одна DLL може надавати декілька методів зовнішньому модулю. Для того щоб зовнішній модуль міг вибрати конкретний метод, в DLL повинна бути присутня спеціальна секція, яка має заголовок exports. У нашому прикладі цю секцію можна оголосити таким чином:
exports AddOne index 1 name ’CalculateSum’; |
Для експонування методу в секції exports наводиться його назва (AddOne), після якого слідує або службове слово index з цілочисловим ідентифікатором після нього (ідентифікатор повинен бути більше нуля), або службове слово name з текстовим ідентифікатором, або обидва разом - як в даному випадку . Зовнішній модуль може звертатися до конкретного методу як за індексом, так і по імені.
Статичне і динамічне завантаження DLL
Модуль може викликати методи іншого модуля, а той, у свою чергу, - наступного і т.д. Наприклад, додаток викликає DLL, а ця DLL викликає методи інший DLL: так можна формувати довгі ланцюжки викликів. Для виклику методу з іншого модуля необхідно спочатку завантажити його в пам'ять, а потім визначити адресу методу. Існує два способи завантаження і визначення адреси методу - статичний і динамічний.
Function Add1(K:integer):integer; stdcall; external 'Project1.DLL' name 'CalculateSum'; |
При статичному завантаженні для виклику іншого модуля слід в якійсь із секцій описати метод з DLL наступним чином:
або
function Add1(K:integer):integer; stdcall; external 'Project1.DLL' index 1; |
Для тестування необхідно описати в додатку зовнішній метод одним з вищезазначених способів і зробити, наприклад, обробник події OnClick кнопки, поставленої на форму разом з компонентом TEdit:
procedure TForm1.Button1Click(Sender: TObject); var N:integer; begin N:=StrToInt(Edit1.Text); N:=Add1(N); Edit1.Text:=IntToStr(N); end; |
При натисканні кнопки буде викликатися метод з DLL. Зверніть увагу на зміну імен методу: з обробника події OnClick викликається метод з ім'ям Add1. Цей метод експонується в DLL під ім'ям CalculateSum. У реалізації DLL він має назву AddOne.
При такому визначенні методу DLL буде завантажена негайно після старту програми та вивантажено разом з його завершенням. У наведеному вище прикладі слід звернути увагу на те, що після імені динамічної бібліотеки зазначено її розширення (Project1.dll). Така конструкція необхідна для завантаження бібліотеки в Windows NT, оскільки без розширення *. Dll файл не буде знайдено.
При пошуку DLL для завантаження спочатку визначається, чи була дана DLL вже завантажена в пам'ять іншим модулем. Якщо була - то витягується адреса методу і передається з додатком. Якщо ж ні - то операційна система починає її пошук на диску. При цьому, якщо шлях при імені DLL не вказаний в явному вигляді, система шукає бібліотеку в каталозі модуля, який намагається завантажити DLL. Якщо не знаходить, то продовжує пошуки в директоріях WINDOWS і WINDOWS \ SYSTEM (або WINNT, WINNT \ SYSTEM, WINNT \ SYSTEM32). Після цього відбувається пошук в каталогах, визначених у змінній середовища Path. Якщо бібліотека з заданим ім'ям буде знайдена, то вона завантажиться і додаток стартує. Якщо ж ні - відбувається виключення і додаток припинить свою роботу. Додаток припиняє роботу також і в тому випадку, якщо не буде знайдений метод з даними ім'ям (або індексом, якщо він імпортується за індексом).
Динамічне завантаження DLL дозволяє завантажувати бібліотеку тільки в той момент, коли вона потрібна. Крім того, якщо не буде знайдена бібліотека або метод, то це можна проаналізувати і запустити додаток і в цьому випадку. Звичайно, в такій ситуації слід інформувати користувача про неможливість викликати метод з DLL - наприклад, зробивши невидимим пункту меню, який звертається до даного методу. Приклад динамічного завантаження DLL виглядає наступним чином:
type TAddFunction=function(K:
procedure TForm1.Button2Click(Sender: TObject); var Add1:TAddFunction; HLib:THandle; N:integer; begin HLib:=0; try HLib:=LoadLibrary('Project1. if HLib>HINSTANCE_ERROR then begin Add1:=GetProcAddress(HLib,' if Assigned(Add1) then begin N:=StrToInt(Edit1.Text); N:=Add1(N); Edit1.Text:=IntToStr(N); end else ShowMessage('Метод з ім’ям CalculateSum не знайдений'); end else ShowMessage('Не знайдена бібліотека Project1.dll'); finally if HLib>HINSTANCE_ERROR then FreeLibrary(HLib); end; end; |
Спочатку визначається новий процедурний тип, наприклад TAddFunction, який має такий же список параметрів і такі ж домовленості виклику, що й метод в DLL. Delphi в процесі компіляції програми ніяк не може перевірити відповідність процедурного типу методу, що викликається з DLL. Перевірка може бути здійснена тільки під час виконання, а при невідповідності формальних параметрів або невірно зазначеної домовленості виклику відбувається крах стека, що програміст виявить дуже швидко.
Далі в коді програми викликається метод LoadLibrary, який як параметр використовує ім'я бібліотеки. Після успішної відпрацювання даного методу і завантаження бібліотеки в пам'ять показник на завантажену бібліотеку поміщається в змінну HLib. Якщо бібліотеку не вдається знайти або завантажити, то в цю ж змінну поміщається код помилки. Щоб визначити, чи була завантажена бібліотека, змінну HLib слід порівняти з константою HINSTANCE_ERROR, яка визначена в модулі Windows. Якщо бібліотека була успішно завантажена, то здійснюється спроба знайти адресу методу в пам'яті комп'ютера за допомогою виклику методу GetProcAddress. Зазначений метод повертає адресу того методу, ім'я якого зазначено в другому параметрі GetProcAddress. Якщо ж метод не був знайдений, то повертається nil-адресса.
Відповідно виклик методу Add1 здійснюється тільки у випадку, якщо він був успішно знайдений. Потім, оскільки при завантаженні бібліотеки були зайняті системні ресурси, їх необхідно знову повертати операційній системі, вивантаживши бібліотеку з пам'яті. Для цього викликається метод FreeLibrary. При завантаженні DLL проводиться підрахунок посилань, а саме: при кожному успішному зверненні до методу LoadLibrary в DLL лічильник посилань збільшується на одиницю, а при кожному виклику методу FreeLibrary лічильник посилань зменшується на одиницю. Як тільки лічильник посилань стане рівним нулю, бібліотека вивантажується з пам'яті комп'ютера. Отже, кожному успішному викликом LoadLibrary має відповідати звернення до FreeLibrary - інакше DLL не вивантажити з пам'яті комп'ютера навіть після закінчення роботи програми. Тому дані методи були поміщені в захищений блок try ... finally .. end; - це гарантує виклик методу FreeLibrary, якщо відбувається виключення.
Метод GetProcAddress для завантаження DLL може також використовувати індекси. Для прикладу, представленого вище, в якому метод AddOne експонується з індексом 1, використання індексів в GetProcAddress виглядає наступним чином:
Add1:=GetProcAddress(HLib, |
При завантаженні DLL здійснюється резервування пам'яті, необхідної для збереження коду методів. Крім того, резервується місце для всіх глобальних змінних і виконуються секції ініціалізації в модулях DLL. Якщо інший процес також намагається завантажити DLL, то знову відбувається резервування пам'яті для зберігання глобальних змінних. Проте копіювання методів DLL не здійснюється; також не виконується і секція ініціалізації. Іншими словами, одна копія методу в ОЗУ обслуговує кілька додатків. Глобальні змінні є унікальними для кожного додатку, і якщо один додаток змінить їх значення за допомогою виклику якогось методу, то інший додаток цього не помітить. Оскільки секція ініціалізації виконується тільки при першому завантаженні DLL, її не можна використовувати для установки початкових значень глобальних змінних.
Розглянемо переваги використання DLL: