Примеры статического и динамического связывания DLL

Продолжим работу с DLL и напишем тестирующее приложение. Чтобы более гибко переключаться между отлаживаемой библиотекой и кодом теста, объединим с помощью Менеджера проекта модуль DLL и тестирующий проект в одну группу. Сначала рассмотрим реализацию статического связывания.

1.Выполним команду Вид/Менеджер проекта. Откроется окно Менеджера проекта с одной вершиной MyDLL.dll. Развернем её (рис.10.5) и снова свернем.

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.5 – окно Менеджера проекта с загруженной DLL

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.6 – в окне Менеджера проекта появилась новая вершина

2.В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма. Выполним команду Вид/Менеджер проекта и увидим, что в окне Менеджера проекта появилась вершина Project1.exe, соответствующая создаваемому тестовому приложению (рис.10.6).

3.Выполним команду Файл/Сохранить проект как и сохраним модуль тестового приложения под именем UTestDLL, а проект – под именем PTestDLL.

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.7 – окно Менеджера проекта с загруженной DLL и тестом

4.Введем команду Файл/Сохранить все и сохраним проект под именем TESTDLL. Снова выполним команду Вид/Менеджер проекта и в окне Менеджера проекта увидим загруженную DLLи вершину PTestDLL.exe, соответствующую создаваемому тестовому приложению (рис.10.7).

5.Разместим на форме окно редактирования Edit1 и кнопку Button1(рис.10.8).

а) б)

Примеры статического и динамического связывания DLL - student2.ru Примеры статического и динамического связывания DLL - student2.ru

Рис.10.8 – тестовое приложение DLL: исходный текст в окне (а)

и результат кодировки (б)

В модуле приложения в обработчик щелчка на кнопке поместим оператор:

Edit1->Text = Code_Dec(Edit1->Text.c_str(),’A’);

Он берет текст, занесенный пользователем в окно редактирования Edit1, кодирует его с помощью функции Code_Dec и возвращает закодированную строку вEdit1.

6.Включим в модуль приложения после директивы препроцессора #pragma hdrstop директиву, подключающую заголовочный файл библиотеки:

#include “UMyDLL.h”

Приведем заголовочный файл модуля UTestDLL.h:

//---------------------------------------------------------------------------

#ifndef UTestDLLH

#define UTestDLLH

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

//---------------------------------------------------------------------------

class TForm1 : public TForm

{

__published: // IDE-managed Components

TEdit *Edit1;

TButton *Button1;

void __fastcall Button1Click(Tobject *Sender);

private: // User declarations

public: // User declarations

__fastcall TForm1(TComponent* Owner);

};

//---------------------------------------------------------------------------

extern PACKAGE TForm1 *Form1;

//---------------------------------------------------------------------------

#endif

и файл реализации модуля UTestDLL.cpp:

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include “UMyDLL.h”

#include “UTestDLL.h”

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource “*.dfm”

TForm1 *Form1;

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(Tcomponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(Tobject *Sender)

{

Edit1->Text = Code_Dec(Edit1->Text.c_str(),’A’);

}

//---------------------------------------------------------------------------

7.Осталось подключить к тестирующему приложению файл .lib, обеспечивающий статическое связывание библиотеки. Для этого сначала нужно активизировать приложение в окне Менеджера проекта, выделив вершину проекта PTestDLL.exe и нажав на кнопку Активизировать (рис.10.9).

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.9 – подготовка к активизированию приложения

8.Введём команду Проект/Добавить к проекту… . Во всплывшем диалоговом окне выберем шаблон файлов «Файл библиотеки (*.lib)» и выберем файл MyDLL.lib. Файл библиотеки окажется включенным в тестирующее приложение (рис.10.10). Это обеспечит при запуске приложения статическое связывание библиотеки.

9.Теперь можно сохранить проект, откомпилировать и выполнить его. Результаты работы приложения показаны на рис.10.8.

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.10 – файл библиотеки включен в тестирующее приложение

10.Перейдем к рассмотрению варианта динамического связывания. В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма.

11.Выполним команду Файл/Сохранить проект как и сохраним (в том же каталоге, где находится DLL) модуль под именем UTestDLL2, а проект – под именем PTestDLL2. С помощью окна Менеджера проекта убедимся, что второе тестирующее приложение добавлено (рис.10.11).

Примеры статического и динамического связывания DLL - student2.ru

Рис.10.11 - окно Менеджера проекта с загруженной DLL,

первым тестом PTestDLL.exe и вторым тестом PTestDLL2.exe

Примеры статического и динамического связывания DLL - student2.ru Примеры статического и динамического связывания DLL - student2.ru

Рис.10.12 – тестовое приложение динамического связывания DLL

12.Разместим на форме окно редактирования Edit1 и три кнопки Button1,2,3 (рис.10.12). Кнопка Загрузить (имя BLoad) будет обеспечивать загрузку DLL, кнопка Выгрузить (имя BFree) будет выгружать библиотеку из памяти, а кнопка Кодировать/Декодировать (имя Button1) аналогична той, которая была в первом приложении.

13.В заголовочном файле второго приложения в описание класса формы нужно внести объявления (см. ниже).

//---------------------------------------------------------------------------

#ifndef UTestDLL2H

#define UTestDLL2H

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

//---------------------------------------------------------------------------

class TForm2 : public TForm

{

__published: // IDE-managed Components

TEdit *Edit1;

TButton *Button1;

TButton *BLoad;

TButton *BFree;

void __fastcall BLoadClick(TObject *Sender);

void __fastcall BFreeClick(TObject *Sender);

void __fastcall Button1Click(TObject *Sender);

private: // User declarations

// Объявление указателя на DLL

HINSTANCE dllInstance;

// Объявление указателя на функцию

typedef char* (__import FType(char *, char));

FType * C_D;

public: // User declarations

__fastcall TForm2(TComponent* Owner);

};

//---------------------------------------------------------------------------

extern PACKAGE TForm2 *Form2;

//---------------------------------------------------------------------------

#endif

//---------------------------------------------------------------------------

Переменная dllInstanceбудет содержать указатель на загруженный модуль DLL. Вводимый тип FType соответствует типу функции Code_Dec, которая будет вызвана из библиотеки. А переменная C_D будет содержать указатель на эту функцию.

14.Приведем файл реализации модуля формы второго приложения:

#include <vcl.h>

#pragma hdrstop

#include "UTestDLL2.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm2 *Form2;

//---------------------------------------------------------------------------

__fastcall TForm2::TForm2(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TForm2::BLoadClick(TObject *Sender)

{

// Загрузка DLL

dllInstance = LoadLibrary("MyDLL.dll");

if(dllInstance)

// получение указателя на функцию

C_D = (FType *)GetProcAddress(dllInstance, "_Code_Dec");

else ShowMessage(«Не удалось загрузить 'MyDLL.dll'»);

}

//---------------------------------------------------------------------------

void __fastcall TForm2::BFreeClick(TObject *Sender)

{

// выгрузка DLL

FreeLibrary(dllInstance);

C_D = NULL;

}

//---------------------------------------------------------------------------

void __fastcall TForm2::Button1Click(TObject *Sender)

{

if (C_D)

Edit1->Text = C_D(Edit1->Text.c_str(),'A');

else ShowMessage(

" Функция 'Code_Dec' из 'MyDLL.dll' недоступна");

}

//---------------------------------------------------------------------------

Обработчик BLoadClick загружает библиотеку функцией LoadLibrary. Если загрузить библиотеку не удалось, то значение dllInstance окажется равным NULL. В этом случае пользователю выдается сообщение «Не удалось загрузить 'MyDLL.dll'». А если загрузка прошла успешно, то методом GetProcAddress в переменную C_D заносится указатель на импортируемую функцию.

Обработчик Button1Click обеспечивает вызов функции с помощью указателя C_D. А если указатель равенNULL(например, в библиотеке не оказалось требуемой функции, что в нашем случае невозможно, или перед выполнением Button1Clickне была загружена библиотека, что в нашем случае возможно), то пользователю выдается соответствующее сообщение об ошибке.

Обработчик BFreeClick выгружает функцией FreeLibrary из памяти модуль DLL и обнуляет указательC_D.

15.Выполним данное приложение и убедимся, что все работает нормально (рис.10.12).

16.Проведите эксперимент, размещая исполняемый файл тестового приложения и файл библиотеки в различных каталогах.

Контрольные вопросы

1.Что представляет собой динамически присоединяемая библиотека DLL?

2.Расскажите о назначении DLL. Как она может использоваться?

3.Расскажите о способах связи DLL с приложением. Какой способ предпочтительнее и почему?

4.Как реализовать статическое связывание библиотеки с приложением?

5.Как реализовать динамическое связывание библиотеки с приложением?

6.Какие указатели и как используются при динамическом связывании?

7.Расскажите о порядке создания DLL.

8.Как создается и что содержит файл реализации DLL?

9.Что содержит заголовочный файл библиотеки?

10.Когда и как заголовочный файл библиотеки включается в библиотеку?

11.Когда и как заголовочный файл библиотеки включается в приложение?

12.Как создается приложение для статического связывания с библиотекой DLL?

13.Как создается приложение для динамического связывания с библиотекой DLL?

14.Как разместить исполняемый файл тестового приложения и файл библиотеки в различных каталогах? Приведите пример.

Наши рекомендации