Шаг 91 - Создание DLL в среде C++ Builder, статическая линковка

Тема достаточно важная, так как при разработке больших проектов код обычно вырастает настолько, что приходится его для повышения надежности раскидывать по разным файлам. Вообще строго говоря для этого есть несколько вариантов - можно использовать библиотеки статической линковки *.lib, использовать *.dll в опять-таки статической или же динамической линковке. Надо сказать, что DLL - библиотека, в основе которой лежит принцип динамической связи в любом случае. Но! При использовании способа, который будет описан ниже все заботы по загрузке, выгрузке и импорте функций из DLL берет на себя C++ Builder.

Для примера создаем тестовое приложение, к нему в проектную группу добавляем "DLL Wizard". Опции этого окна таковы:

В общем создали мы проект. Смотрим. Первым делом в глаза бросается обширный комментарий, общий смысл которого заключается в том, что при использовании функций, которые экспортируют описания функций, среди параметров которых встречается AnsiString, исходник приложения, которое использует такое DLL, должен быть скомпилирован с использованием библиотеки MEMMGR.LIB. Ну мы особо выкаблучиваться не будем, и строковые параметры будем передавать как char*. Обратите внимание, что ничто не запрещает использование строк AnsiString внутри кода DLL. Не забудьте только прокомпилировать проект в режиме Release и со снятой галочкой Use Dynamic RTL.

Так вот. Продолжаем. Пишем код какой-нибудь функции и какой-нибудь завалящий класс в наш юнит. Обратите внимание, что заголовочный файл по умолчанию не создается, посему при открытии Open Source/Header File нам предлагается создать новый файл. Что мы и делаем, а затем сохраняем его под расширением *.h. Соответственно имеем исходники:

//--------------------------------------------------------
#define _DLLMAINCPP		// обратите на эту строчку внимание
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
#include "dllmain.h"
#pragma argsused

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        return 1;
}

void Message(char* p)
{
	Application->MessageBox(p,"From DLL",IDOK);
}
void test::hello(char* p)
{
	Message(p);
}
//------------------------------------------------

Комментарий я с Вашего позволения выкинул. Теперь по поводу заголовочного файла. Все экспортируемые функции при сборке должны иметь спецификатор _export или __declspec(dllexport), а импортируемые _import или __declspec(dllimport). То есть если файл заголовка используется самим DLL, то перед функциями должен стоять один из пары первых спецификаторов, а если использующим DLL приложением, то один из пары вторых. Проще всегод это сделать так:

#if defined(_DLLMAINCPP)
#  define DLL_SPEC __declspec(dllexport)
#else
# if defined(_APPMAINCPP)
#  define DLL_SPEC __declspec(dllimport)
# else
#  define DLL_SPEC
# endif
#endif

void DLL_SPEC Message(char *s);
DLL_SPEC class test
{
public:
	void hello(char* s);
};

Теперь собираем приложение, присоединяем получившийся *.lib файл к проекту приложения, добавлям:

#define _APPMAINCPP
#include "dllmain.h"

... в главный файл, собираем, запускаем и пробуем.

В следующем шаге мы рассмотрим возможность использования файлов библиотек напрямую.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Аванесов Самвел - 07.02.2004