Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
РАБ_C++BUILDER.doc
Скачиваний:
100
Добавлен:
02.06.2015
Размер:
7.07 Mб
Скачать

Статическое и динамическое связывание dll с приложением

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

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

Статическое связывание подразумевает, что для DLL создан специальный файл описаний импортируемых функций (import library file). Этот файл имеет расширение .lib и то же имя, что и соответствующая DLL, и должен быть связан с приложением на этапе компиляции.

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

Для вызова библиотечной функции надо сначала загрузить библиотеку функцией LoadLibrary API Windows. Затем с помощью функции GetProcAddress надо получить указатель на нужную функцию библиотеки. Только после этого можно выполнять функцию. А затем с помощью функции FreeLibrary надо выгрузить библиотеку из памяти.

Предположим, создана библиотека mydll.dll, содержащая некоторую функцию char* MyFunction(char*). Тогда для загрузки DLL надо выполнить оператор вида:

//загрузка DLL

HINSTANSE dllInstanse = LoadLibrary(“mydll.dll”);

Получить указатель на импортируемую функцию можно следующим кодом:

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

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

FType * MyFunc;

MyFunc = (FType*) GetProcAddress(dllInstanse, “_MyFunction”);

Объявление typedef вводит пользовательский тип (тип-функция) с произвольным именем FType. Введенный тип используется для задания типа указателя на функцию MyFunc. Для получения значения этого указателя используется функция API Windows GetProcAddress. Она принимает в качестве параметров указатель на загруженный модуль DLL и имя функции, а возвращает указатель на функцию. Этот указатель приводится к типу указателя на используемую функцию библиотеки.

Вызов функции осуществляется с помощью указателя на неё:

//вызов функции

char* S = MyFunc(“Привет!”);

Когда работа с DLL завершена, её можно выгрузить из памяти оператором вида:

//выгрузка DLL

FreeLibrary(dllInstanse);

Если нет острой необходимости использовать именно динамическое связывание, лучше всегда использовать статическое связывание.

Создание dll

Создание своей DLL начинается с выполнения команды Файл/Новый/Другое и выбором в окне Новые элементы на странице Новый пиктограммы Мастер DLL. На форме появляется окно задания опций создания DLL (рис.10.1).

Рис.10.1 - окно задания опций создания DLL

В окне выбран язык DLL – C++; индикатор Исп.VCL позволит создать DLL, которая может содержать компоненты библиотеки VCL. При этом в модуль включится файл vcl.h и установятся опции компоновки, обеспечивающие совместимость с объектами VCL. Индикатор VC++ Style DLL обеспечит создание DLL в стиле Microsoft Visual C++. После установки опций согласно рис.10.1 и щелчка на OK попадают в окно Редактора Кода с заготовкой модуля DLL. В тексте комментария заготовки рекомендуется при передаче строк в функцию и из функции использовать тип char*, а не, например, AnsiString. Приведем пример такой функции для включения в DLL.

char * Code(char * s, char Key)

{

for (int i = 0; ; i++)

{

if (s[i] == ’\0’) break;

s[i] = s[i] ^ Key;

}

return s;

}

Функция воспринимает строку типа char* и ключ типа char и возвращает зашифрованную этим ключом строку. Кодировка такова: каждый символ строки складывается с ключом по операции исключающее ИЛИ. При дешифровании достаточно с закодированной строкой выполнить ту же операцию с тем же ключом. Далее имя функции заменено на Code_Dec.

Текст комментария нужно удалить.

В заготовке модуля DLL строка #include <vcl.h> подключает заголовочный файл vcl.h. Он будет нужен, если при создании DLL будут использоваться какие-то классы, формы, функции, связанные с библиотекой визуальных компонентов. Иначе – эту строку можно удалить из текста DLL.

В конце текста заготовки модуля DLL используется функция DllEntryPoint, необходимая для загрузки и выгрузки библиотеки. Она создает дескриптор hinst создаваемой DLL. Он требуется при выполнении некоторых функций, например, LoadIcon, LoadCursor и др. В этих случаях можно использовать параметр hinst для создания соответствующей глобальной переменной.

Перед описанием функции DllEntryPoint необходимо вставить директиву включения головного файла #includeUMyDLL.h. Файла UMyDLL.h пока нет, - он будет создан ниже.

После выполнения последовательности очевидных действий и сохранения модуля под именем UMyDLL, а проекта – под именем MyDLL, получим файл реализации с именем UMyDLL.cpp (рис.10.2).

Рис.10.2 – файл реализации UMyDLL.cpp библиотеки

Теперь создадим заголовочный файл UMyDLL.h библиотеки. Выполним команду Файл/Новый/Другое и выберем в окне Новые элементы на странице Новый пиктограмму Файл Заголовка. Сохраним файл под именем UMyDLL.h. В появившееся окно Редактора Кода загрузим текстовый файл – запишем в файл следующий код (рис.10.3):

Рис.10.3 – заголовочный файл UMyDLL.h библиотеки

Идентификаторы _UMYDLL_H и DLL_EI могут быть любыми.

Логика работы первого фрагмента в заголовочном файле следующая: если не определен идентификатор _UMYDLL_H (ifndef _UMYDLL_H), то он определяется, т.е. замещается пустой строкой (define _UMYDLL_H ). Этот фрагмент не позволяет многократно включать заголовочный файл в файлы реализации библиотеки или приложения. Логика работы второго фрагмента в заголовочном файле следующая: если определен идентификатор __DLL__ (ifdef __DLL__), то идентификатор DLL_EI раскрывается как __declspec(dllexport); если же идентификатор __DLL__ не определен (else), то идентификатор DLL_EI раскрывается как __declspec(dllimport). C++Builder автоматически определяет __DLL__ в случае, если создается проект DLL, и не определяет этот идентификатор при создании объекта приложения. Таким образом, в зависимости от того, включается ли заголовочный файл в библиотеку или в приложение, он будет выглядеть по-разному. При компиляции библиотеки строка, определяющая функцию Code_Dec, после раскрытия макроса будет восприниматься как

extern "C" char * __declspec(dllexport) Code_Dec (char *s, char Key);

Здесь конструкция __declspec(dllexport) означает, что функция может экспортироваться из библиотеки, т.е. может вызываться внешними приложениями. Подобным же образом должны быть перечислены все функции DLL, предназначенные для прямого использования в приложениях. Помимо таких функций в DLL могут быть вспомогательные функции-утилиты, предназначенные только для использования другими функциями. Подобные утилиты не должны определяться как экспортируемые.

Когда тот же заголовочный файл включается в приложение, то эта же строка после раскрытия макроса имеет другой вид:

extern "C" char * __declspec(dllimport) Code_Dec (char *s, char Key);

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

Продолжим создание DLL.

Выполним команду Проект/Опции и в окне опций проекта на странице Компоновщик убедимся, что включен индикатор опции Создать библиотеку импорта. Эта опция обеспечит при создании DLL автоматическую генерацию файла .lib, необходимого для статического присоединения DLL к проектам. Индикатор опции Создать библиотеку импорта будет доступен только при наличии проекта MyDLL, файлов UMyDLL.cpp и UMyDLL.h.

Выполним команду Проект/Создать MyDLL. В результате будут созданы файлы MyDLL.dll и MyDLL.lib. Убедитесь в этом.

Рис.10.4 – сообщение об ошибке

Попробуем протестировать создаваемую DLL и выполним команду Запуск/Запустить (F9). Получим сообщение об ошибке (рис.10.4), которое означает, что сначала с помощью команды Запуск/Параметры… нужно определить хост – тестирующее приложение.