Министерство общего и профессионального образования

Российской Федерации

Томский политехнический университет

УТВЕРЖДАЮ

Декан АВТФ

_________________

«____»_____________ 2007 г.

Создание DLL библиотеке в среде С++Builder

Методические указания к написанию программного кода лабораторных работ по курсу «Технология разработки ПО»

для студентов

специальности 220400 «Программное обеспечение

вычислительной техники и автоматизированных систем»

Томск 2007
УДК 681.3

Создание DLL библиотеке в среде С++Builder Методические указания к написанию программного кода лабораторных работ по курсу «Технология разработки ПО» для студентов специальности 220400 «Программное обеспечение вычислительной техники и автоматизированных систем». - Томск: изд. ТПУ, 2007. – 8 с.

Составители:

Введение

DLL (dynamic link library) – это один или несколько участков кода, хранимых в файле с расширением dll. Код находящийся в dll файле может быть вызван из исполняемой программы, но сама dll не является автономной программой. Т. е. dll можно рассматривать как дополнительный вспомогательный файл основной программы.

Типы DLL

Существует два типа DLL:

1.  DLL кода;

2.  DLL ресурсов;

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

DLL может содержать код в двух видах:

1.  в виде отдельных функций. Эти функции могут вызываться в исполняемой программе, которая использует dll.

2.  классы С++. Вместо доступа к отдельным функциям задается экземпляр класса и вызывать эти функции-члены класса через объект класса.

НЕ нашли? Не то? Что вы ищете?

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

Статическая загрузка DLL означает, что DLL автоматически загружается при запуске приложения, обращающегося к этой DLL. DLL содержит экспортируемые функции. Описание этих функций находиться в специальном файле, который называется файлом библиотеке импорта. Файл библиотеке импорта имеет имя совпадающее с DLL и расширение. lib.

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

Динамическая загрузка означает, что DLL загружается при необходимости использования и может быть выгружена при окончании использования. Достоинство такого метода в том что DLL находиться в памяти только тогда когда ее использование необходимо, а так же приложение, использующее DLL загружается быстрее поскольку не требуется загружать сразу весь необходимый код. Главный недостаток динамической загрузки в том, что чтобы выполнить работу с функцией необходимо выполнить большее количество работы. Т. е. вначале необходимо загрузить DLL используя специальную функцию LoadLibrary(). Затем окончив работу с DLL необходимо ее выгрузить при помощи FreeLibrary(). И кроме этого нужно использовать функцию GetProcAdress() для того чтобы получить указатель на функцию из DLL, чтобы вызвать эту функцию.

В методичке будет подробно рассмотрена статическая загрузка DLL.

Экспорт функций и классов

Функции DLL делятся на три категории:

1.  Функции, вызываемые внутри DLL;

Функции, вызываемые внутри DLL, не требуют специального оформления и объявляются точно так же как и любые другие функции. Такие функции могут быть вызваны в пределах DLL, но не могут вызываться из вне.

2.  Функции, вызываемые из вне DLL;

Эти функции есть возможность вызывать из вне (DLL) в результате экспорта. Они могут вызываться функциями самой DLL, а так же каким-то внешним приложением. Для экспорта функций при создании DLL используется служебное слово __export.

#Пример исходного кода:

int __export F_Summ(int a, int b) {

return a + b;

}

3.  Функции, которые является методами (членами) классов и вызываются из вне.

Экспорт классов осуществляется в основном так же, только слово __export помещается при объявлении класса.

#Пример исходного кода:

class __export TPoint{

int x, y;

void Draw();

…..

…..

} ;

Импорт функций и классов

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

#Пример в случае с функцией F_Summ(int a, int b)

extern “C” void __import F_Summ(int a, int b)

//

// затем

//

F_Summ(5,5);

Модификатор __import указывает компилятору, что ссылка на объявленную функцию содержится в библиотеки импорта.

Импорт классов выполняется аналогично.

#Пример импорта класса:

class __import TPoint{

public:

int x, y;

void Draw();

};

// после этого

TPoint A;

A. x = 6;

A. y = 7;

A. Draw();

Главное при написании DLL – это правильно выполнить экспорт и импорт.

Другой способ!!!

Для приложений c++builder разработан другой способ экспорта и импорта классов. Вместо использования функций __export\__import лучше применять ключевое слово __declspec(). Используя ключевое слово __declspec() не нужно заботиться о местепостановки модификатора. То есть оба варианта будут верными:

__declspec(dllexport) void My_F();

void __declspec(dllexpor) My_F();

Кроме этого как уже было рассмотрено выше. Функцию приходиться описывать дважды, в DLL и приложении с отличием в заголовках (__export, __import). Для того чтобы обойтись одним заголовком, можно воспользоваться макросами:

#if defined(BUILD_DLL)

# define DLL_EXP __declspec(dllexport)

#else

# if defined (BUILD_APP)

# define DLL_EXP __declspec(dllimport)

# else

# define DLL_EXP

# endif

#endif

// это пример функции

extern "C" int DLL_EXP F_Summ(int x, int y);

// это пример класса

class DLL_EXP My_Class{

public:

int x, y;

My_Class(int X, int Y);

int Sum(); } ;

Рассмотрим подробнее строки макроса. Проверяем объявлена ли константа BUILD_DLL, если да, то присваиваем константе DLL_EXP -- __declspec(dllexport). Если нет, то запрашиваем определена ли константа BUILD_APP. Если да то определяем константу DLL_EXP как __declspec( dllimport), если нет то определяем DLL_EXP. То есть другими словами: если определена константа BUILD_DLL, то имя DLL_EXP раскрывается в текст declspec(dllexport). Если определена константа BUILD_APP, то DLL_EXP раскрывается в текст __declspec( dllimport).

Таким образом можно использовать один и тот же заголовок (DLL_EXP) для DLL и приложения. Работая с DLL надо сделать так чтобы константа BUILD_DLL была определена.

#define BUILD_DLL

#include “my_dll. h”

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

#define BUILD_APP

#include “my_dll. h”

В этом случае DLL_EXP раскрывается в текст __declspec( dllimport) и тогда все функции и классы будут импортируемыми. Это позволяет использовать один и тот же заголовок как для создания приложения вызывающего DLL, так и для самой DLL.

Создание DLL с помощью репозитария объектов

В c++Builder DLL создается при помощи репозитария объектов.

1.  В главном меню выберите File → New.

2.  Щелкните на значке DLL в появившимся окошке. На экране появиться редактор кода с со следующим файлом в окне редактирования:

//

#include <vcl. h>

#pragma hdrstop

//

// Important note about DLL memory management when your DLL uses the

// static version of the RunTime Library:

//

// If your DLL exports any functions that pass String objects (or structs/

// classes containing nested Strings) as parameter or function results,

// you will need to add the library MEMMGR. LIB to both the DLL project and

// any other projects that use the DLL. You will also need to use MEMMGR. LIB

// if any other projects which use the DLL will be perfomring new or delete

// operations on any non-TObject-derived classes which are exported from the

// DLL. Adding MEMMGR. LIB to your project will change the DLL and its calling

// EXE's to use the BORLNDMM. DLL as their memory manager. In these cases,

// the file BORLNDMM. DLL should be deployed along with your DLL.

//

// To avoid using BORLNDMM. DLL, pass string information using "char *" or

// ShortString parameters.

//

// If your DLL uses the dynamic version of the RTL, you do not need to

// explicitly add MEMMGR. LIB as this will be done implicitly for you

//

//

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)

{

return 1;

}

3.Создайте новый файл допустим My_class. h, в котором будет содержаться прототипы ваших функций и описание класса(ов).

4.Сохраните ваш проект и файл в одну папку путем File → Save Project As (Например с именем My_dll)

5.В My_class. h вставьте строки макроса:

#if defined(BUILD_DLL)

# define DLL_EXP __declspec(dllexport)

#else

# if defined (BUILD_APP)

# define DLL_EXP __declspec(dllimport)

# else

# define DLL_EXP

# endif

#endif

Затем опишите все необходимые функции и классы в этом же файле My_class. h

// это пример функции

extern "C" int DLL_EXP F_Summ(int x, int y);

// это пример класса

class DLL_EXP My_Class{

public:

int x, y;

My_Class(int X, int Y);

int Sum(); } ;

6. Сохраните изменение.

7. Переключитесь на окно My_dll. cpp и добавьте следующие строки перед функцией DllEntryPoint

#define BUILD_DLL

#include "my_class. h"

Выполните реализацию ваших функций. Таким образом, содержание файла My_dll. cpp будет следующим

//

#include <vcl. h>

#pragma hdrstop

//

// Important note about DLL memory management when your DLL uses the

// static version of the RunTime Library:

//

// If your DLL exports any functions that pass String objects (or structs/

// classes containing nested Strings) as parameter or function results,

// you will need to add the library MEMMGR. LIB to both the DLL project and

// any other projects that use the DLL. You will also need to use MEMMGR. LIB

// if any other projects which use the DLL will be perfomring new or delete

// operations on any non-TObject-derived classes which are exported from the

// DLL. Adding MEMMGR. LIB to your project will change the DLL and its calling

// EXE's to use the BORLNDMM. DLL as their memory manager. In these cases,

// the file BORLNDMM. DLL should be deployed along with your DLL.

//

// To avoid using BORLNDMM. DLL, pass string information using "char *" or

// ShortString parameters.

//

// If your DLL uses the dynamic version of the RTL, you do not need to

// explicitly add MEMMGR. LIB as this will be done implicitly for you

//

USEFILE("my_class. h");

// это мы делаем определенной константу и DLL_EXP

#define BUILD_DLL

#include "my_class. h"

//

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)

{

return 1;

}

//

// это описание методов класса

My_Class::My_Class(int X, int Y) {

x = X;

y = Y;

}

//

int My_Class::Sum() {

return x + y;

}

//--

My_Class::~My_Class(){

x = 0;

y = 0;

}

// конец описания методов класса

//***********************************************

// это описание ФУНКЦИИ отдельной

int DLL_EXP F_Summ(int x, int y){

return x + y;

}

//***********************************************

8. Сохранить изменения

9. Проверить что опция Project → Options → … была включена

10. Выбрать Project → Build All и убедиться что в папке с проектом появятся файлы My_dll. dll и файлы My_dll. lib

11. Закрыть проект и создать новый проект, чтобы протестировать приложение.

12. В тестовом приложении в Unit1.cpp записать следующие строки после строк (#pragma):

#define BUILD_APP

#include "my_class. h"

13. Добавьте к созданному новому проекту файл My_dll. lib путем нажатия пункта меню Project → Add to Project и выбора файла My_dll. lib в открывшемся окне.

13. На форму поместите два поля (компонент TEdit) и две кнопки.

Рисунок 1. Форма тестового приложения

14. Дописать обработчики событий на нажатие кнопки Button1 кодом, который демонстрирует работу класса описанного в DLL.
15. Дописать обработчики событий на нажатие кнопки Button2 кодом, который демонстрирует работу отдельной функцией (F_Summ) описанной в DLL.
Таким образом после выполнения шагов 15 и 16 код Unit1.cpp выглядит следующим образом:

//

#include <vcl. h>
#pragma hdrstop
#include "Unit1.h"
//
#pragma package(smart_init)
#pragma resource "*.dfm"

//это обязательно
#define BUILD_APP
#include "my_class. h"

TForm1 *Form1;
//
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{

}
//
void __fastcall TForm1::Button1Click(TObject *Sender)
{

My_Class A(4 , 5); // объект класса
Edit1->Text = IntToStr( A. Sum()) ; }

//
void __fastcall TForm1::Button2Click(TObject *Sender)
{

Edit2->Text = IntToStr( F_Summ(10 , 10) );

}

//

16. Сохраните проект, скопируйте в папку с проектом My_dll. dll и файл my_class. h и запустите приложение.

Варианты заданий:

1.  Создайте DLL содержащий код описания класса TArray и реализуйте следующие три метода этого класса: Count – количество элементов в массиве; Max – поиск максимального элемента в массиве. Sum – cумма элементов в массиве.

2.  Создайте DLL содержащий код описания класса TString и реализуйте следующие три метода этого класса: Length – количество символов в строке; Find(char ch) – поиск первого вхождения символа ch в строку; Upper – перевод всех символов нижнего регистра в верхний.

3.  Создайте DLL содержащий код описания класса TYesNo и реализуйте следующие три метода этого класса: GiveAnswer – возвращает случайным образом строку «Yes» или «No»; LastAnswer() – возвращает последний ответ который был дан; ChangeYesNo(char * Y, char * N) заменяет соответственно строку «Yes» на Y и «No» на N. регистра в верхний.

Представить результаты работы в тестовом приложении.
Список дополнительной литературы:

1.  Хендерсон К «Borland C++ Builder. Освой самостоятельно»

2.  Сурков К «Программирование в среде C++ Builder», Минск – 2004 г.