Создание 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необходимо вставить директиву включения головного файла #include “UMyDLL.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), которое означает, что сначала с помощью команды Запуск/Параметры… нужно определить хост – тестирующее приложение.