Продолжим работу с DLL и напишем тестирующее приложение. Чтобы более гибко переключаться между отлаживаемой библиотекой и кодом теста, объединим с помощью Менеджера проекта модуль DLL и тестирующий проект в одну группу. Сначала рассмотрим реализацию статического связывания.
1. Выполним команду Вид/Менеджер проекта. Откроется окно Менеджера проекта с одной вершиной MyDLL.dll. Развернем её (рис.10.5) и снова свернем.
Рис.10.5 – окно Менеджера проекта с загруженной DLL
Рис.10.6 – в окне Менеджера проекта появилась новая вершина
2. В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма. Выполним команду Вид/Менеджер проекта и увидим, что в окне Менеджера проекта появилась вершина Project1.exe, соответствующая создаваемому тестовому приложению (рис.10.6).
3. Выполним команду Файл/Сохранить проект как и сохраним модуль тестового приложения под именем UTestDLL, а проект – под именем PTestDLL.
Рис.10.7 – окно Менеджера проекта с загруженной DLL и тестом
4. Введем команду Файл/Сохранить все и сохраним проект под именем TESTDLL. Снова выполним команду Вид/Менеджер проекта и в окне Менеджера проекта увидим загруженную DLL и вершину PTestDLL.exe, соответствующую создаваемому тестовому приложению (рис.10.7).
5. Разместим на форме окно редактирования Edit1 и кнопку Button1 (рис.10.8).
а) б)
Рис.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).
Рис.10.9 – подготовка к активизированию приложения
8. Введём команду Проект/Добавить к проекту…. Во всплывшем диалоговом окне выберем шаблон файлов «Файл библиотеки (*.lib)» и выберем файл MyDLL.lib. Файл библиотеки окажется включенным в тестирующее приложение (рис.10.10). Это обеспечит при запуске приложения статическое связывание библиотеки.
9. Теперь можно сохранить проект, откомпилировать и выполнить его. Результаты работы приложения показаны на рис.10.8.
Рис.10.10 – файл библиотеки включен в тестирующее приложение
10. Перейдем к рассмотрению варианта динамического связывания. В окне Менеджера проекта нажмем кнопку Новый и в окне Новые элементы на странице Новый выберем пиктограмму Приложение. Нажмем ОК. На экране появится пустая форма.
11. Выполним команду Файл/Сохранить проект как и сохраним (в том же каталоге, где находится DLL) модуль под именем UTestDLL2, а проект – под именем PTestDLL2. С помощью окна Менеджера проекта убедимся, что второе тестирующее приложение добавлено (рис.10.11).
Рис.10.11 - окно Менеджера проекта с загруженной DLL,
первым тестом PTestDLL.exe и вторым тестом PTestDLL2.exe
Рис.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. Как разместить исполняемый файл тестового приложения и файл библиотеки в различных каталогах? Приведите пример.