Как сделать программу dll

Передо мной возникла задача написать загрузчик библиотек, имеющий возможность предоставить какие-то интерфейсные функции внешней динамической библиотеке. Решение должно быть максимально кроссплатформенно (как минимум, работать на Linux и Windows). Загружаться должны библиотеки, написанные на различных языках программирования, поддерживающих создание динамических библиотек. В качестве примера были выбраны языки C и Pascal.

Решение

Основной загрузчик библиотек написан на языке C. Для того, чтобы загружаемые библиотеки имели возможность использовать функции основной программы, основная программа разделена на 2 части: на основной и подгружаемый модули. Основной модуль нужен просто для запуска программы, подгружаемый модуль — это также динамическая библиотека, связываемая с основным модулем во время его запуска. В качестве компиляторов были выбраны gcc (MinGW для Windows) и fpc.
Здесь будет приведён упрощённый пример программы, позволяющий разобраться в данном вопросе и учить первокурсников писать модули к своей программе (в школе часто преподают именно Pascal).

Загрузчик библиотек


Главный файл загрузчика библиотек выглядит очень просто:

main.c

#include «loader.h» #ifdef __cplusplus extern «C» < #endif int main(int argc, char *argv[]) < if (argc >1) < loadRun(argv[1]); >return 0; > #ifdef __cplusplus > #endif

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

loader.c

#include «loader.h» #include «functions.h» #include #ifndef WIN32 #include #else #include #endif #ifdef __cplusplus extern «C» < #endif void printString(const char * const s) < printf(«String from library: %sn», s); >void loadRun(const char * const s) < void * lib; void (*fun)(void); #ifndef WIN32 lib = dlopen(s, RTLD_LAZY); #else lib = LoadLibrary(s); #endif if (!lib) < printf(«cannot open library ‘%s’n», s); return; >#ifndef WIN32 fun = (void (*)(void))dlsym(lib, «run»); #else fun = (void (*)(void))GetProcAddress((HINSTANCE)lib, «run»); #endif if (fun == NULL) < printf(«cannot load function runn»); >else < fun(); >#ifndef WIN32 dlclose(lib); #else FreeLibrary((HINSTANCE)lib); #endif > #ifdef __cplusplus > #endif

Заголовочные файлы

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

loader.h

#ifndef LOADER_H #define LOADER_H #ifdef __cplusplus extern «C» < #endif extern void loadRun(const char * const s); #ifdef __cplusplus >#endif #endif

Вот интерфейс загрузчика для загружаемых им динамических библиотек (перечень функций, которые динамические библиотеки могут использовать в основной программе):

Не запускается игра Error «Исправлении ошибки — Отсутствует XINPUT1_4.DLL

functions.h

#ifndef FUNCTIONS_H #define FUNCTIONS_H #ifdef __cplusplus extern «C» < #endif extern void printString(const char * const s); #ifdef __cplusplus >#endif #endif

Как видно, здесь всего одна функция printString для примера.

Компиляция загрузчика

Пример недистрибутивной компиляции (в случае Windows в опции компилятора просто нужно добавить -DWIN32):

Читайте также:
Какие конкурентные преимущества сервиса 1с обновление программ вы знаете

$ gcc -Wall -c main.c $ gcc -Wall -fPIC -c loader.c $ gcc -shared -o libloader.so loader.o -ldl $ gcc main.o -ldl -o run -L. -lloader -Wl,-rpath,.

Дистрибутивная компиляция от недистрибутивной отличается тем, что в дистрибутивном случае динамические библиотеки ищутся в /usr/lib и имеют вид lib$(NAME).so.$(VERSION), в случае недистрибутивной компиляции они называются lib$(NAME).so, а ищутся в каталоге запуска программы.

Теперь посмотрим, что у нас получилось после компиляции:

Тут видим, что функции, отмечаемые как U ищутся во внешних динамических библиотеках, а функции, отмечаемые как T предоставляются модулем. Это бинарный интерфейс программы (ABI).

Динамические библиотеки


Теперь приступим к описанию самих динамических библиотек.

Библиотека на языке C


Вот пример простейшей библиотеки на языке C:

lib.c

#include «functions.h» #ifdef __cplusplus extern «C» < #endif void run(void) < printString(«Hello, world!»); >#ifdef __cplusplus > #endif

Здесь везде окружение extern «C» <> нужно для того, чтобы нашу программу можно было компилировать при помощи C++-компилятора, такого, как g++. Просто в C++ можно объявлять функции с одним и тем же именем, но с разной сигнатурой, соответственно в этом случае используется так называемая декорация имён функций, то есть сигнатура функций записывается в ABI. Окружение extern «C» <> нужно для того, чтобы не использовалась эта декорация (тем более, что эта декорация зависит от используемого компилятора).

Компиляция

$ gcc -Wall -fPIC -c lib.c $ gcc -shared -o lib.so lib.o
$ nm lib.so | tail -n 2 U printString 0000043c T run
$ ./run lib.so String from library: Hello, world!

Если мы уберём в нашем модуле окружение extern «C» <> и скомпилируем при помощи g++ вместо gcc, то увидим следующее:

$ nm lib.so | grep run 0000045c T _Z3runv

То есть, как и ожидалось, ABI библиотеки изменился, теперь наш загрузчик не сможет увидеть функцию run в этой библиотеке:

$ ./run lib.so cannot load function run

Библиотека на языке Pascal

Как мы увидели выше, для того, чтобы наш загрузчик видел функции в динамических библиотеках, созданных компилятором C++, пришлось дополнять наш код вставками extern «C» <>, это притом, что C/C++-компиляторы и языки родственные. Что уж говорить про компилятор FreePascal совершенно другого языка — Pascal? Естественно, что и тут без дополнительных телодвижений не обойтись.

Для начала нам нужно научиться использовать экспортированные в C функции для динамических библиотек. Вот пример аналогичного C/C++ заголовочного файла на языке Pascal:

func.pas

unit func; interface procedure printString(const s:string); stdcall; external name ‘printString’; implementation end.

Вот пример самого модуля на языке Pascal:

modul.pas

library modul; uses func; procedure run; stdcall; begin printString(‘Hello from module!’); end; exports run; begin end.

Компиляция

$ fpc -Cg modul.pas Компилятор Free Pascal версии 2.5.1 [2011/02/21] для i386 Copyright (c) 1993-2010 by Florian Klaempfl Целевая ОС: Linux for i386 Компиляция modul.pas Компоновка libmodul.so /usr/bin/ld: warning: link.res contains output sections; did you forget -T? /usr/bin/ld: warning: creating a DT_TEXTREL in a shared object. 13 строк скомпилиpовано, 6.6 сек

Смотрим ABI получившейся библиотеки:
$ nm libmodul.so U printString 000050c0 T run

Читайте также:
Epson l800 очистка головки программа

Как видим, ничего лишнего, однако настораживает предупреждение ld во время компиляции. Находим в гугле возможную причину предупреждения, это связано с компиляцией без PIC (Position Independent Code — код не привязан к физическому адресу), однако в man fpc находим, что наша опция -Cg должна генерировать PIC-код, что само по себе странно, видимо fpc не выполняет своих обещаний, либо я делаю что-то не так.

Теперь попробуем убрать в нашем заголовочном файле кусок name ‘printString’ и посмотрим, что выдаст компилятор теперь:

$ nm libmodul.so U FUNC_PRINTSTRING$SHORTSTRING 000050d0 T run

Как видно, декорация в FreePascal совсем другого рода, чем в g++ и тоже присутствует.
При запуске с этим модулем получаем:

$ ./run libmodul.so ./run: symbol lookup error: ./libmodul.so: undefined symbol: FUNC_PRINTSTRING$SHORTSTRING

А с правильным модулем получаем:
$ ./run libmodul.so String from library: Hello from module!

Вот и всё, своей задачи — использование динамических библиотек, написанных на различных языках — мы достигли, и наш код работает как под Linux, так и под Windows (у самого Mac’а нет, поэтому не смотрел, как там с этим). Кроме того, загруженные библиотеки имеют возможность использовать предоставляемые в основной программе функции для взаимодействия с ней (то есть являются плагинами основной программы).

Сам загрузчик может выполняться в отдельном процессе, чтобы эти плагины не смогли сделать ничего лишнего с основной программой. Соответственно, основная программа может быть написана на любом другом удобном языке программирования (например, на Java или на том же C++).

Литература

  • Загрузка DLL в C++.
  • Linux первые шаги.
  • Создание модуля Apache на языке Pascal.

Источник: habr.com

Создаём DLL на C++

В предыдущей статье мы создавали DLL, используя Delphi, на языке Pascal. Delphi является коммерческой средой разработки, и стоит огромных денег. Потому мы сегодня поменяем среду разработки и язык программирования, а задача останется прежней.

В качестве среды разработки на C++ я выбрал бесплатно распространяемый Microsoft visual studio community 2015. Не берусь назвать причину этого аттракциона неслыханной щедрости со стороны Microsoft, это тема отдельного исследования :).

Вновь мы напишем две функции, и сделаем так, чтобы одна из них была доступна для внешнего вызова из DLL.

Инициализация нового проекта

Необходимо создать новый проект Visual C++ (меню Файл -> Создать -> Проект), используем шаблон консольного приложения.

dll-visual-c-creation

Выбираем и жмем ОК.

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

aplication-parameters-visiual-c-dll

Думаю, тоже не возникнет вопросов с выбором параметров. Остаётся нажать кнопку — «Готово».

Создание файлов проекта DLL.

Добавим в проект два исходных файла. Один с расширением .cpp будет содержать программный код двух наших тестовых функций, а второй .def будет содержать инструкции для билдера.

Читайте также:
Резидентная программа это программа которая

Откройте «обозреватель решений» (ох уж мне эти тонкости перевода интерфейса на русский язык 🙂 ). Если он не открыт, то найдите его через меню «Вид» или нажмите комбинацию Ctrl+Alt+L.

Нужно добавить два файла в секцию «Файлы исходного кода».

add-source-file-dll-project

add-cpp-to-project

Сначала добавим файл .cpp, потом .def.

Пишем код DLL

Запрограммируем код наших функций (.cpp)

Источник: shra.ru

Практика №2. Создание и подключение dll библиотеки.

Ранее было отмечено, что наш проект не только (столько) учебный, но расчитан на создание полноценных программ с минимальными трудозатратами.

Достаточно важной возможностью, можно считать, подключение dll — библиотеки.
В том числе и собственной разработки.

В первом примере мы создадим одну простую функцию внутри dll и вызовем её из нашего проекта.
Не лишним будет уточнить, что мы воспользуемся динамическим способом подключения ( вызова ).
Никаких сомнений в работоспособности примера после вызова функции у нас не останется.

Приступаем к созданию dll.
В Dev-C++ мы выберем: «Файл —> Создать —> Проект.. «


После выбора пункта «Проект» появится такое окно.

Имя «Проект1» измените на свой вариант (например, dll_1) и нажмите кнопку «ОК».
Ниже приведён текст dllmain.cpp файла вновь созданной динамической библиотеки ( dll ).

Наша функция находится в самом низу ( от строки 42 и до строки 46).
В качестве параметра она принимает адрес начала чарового массива (строки символов).
С помощью функции strcar() дописывает в строку этот текст: » ++ Hello ( Привет ! ) from dll_1.dll» . и всё .
Даже не надо возвращать строку, так как она никуда не перемещалась.

Теперь рассмотрим содержимое файла main.cpp нашего проекта.

Если мы правильно подключим dll-библиотеку и эту функцию из нашего проекта, то сможем считать сегодняшний день результативным.
Что бы всё прошло гладко и снизить до ноля риск ошибки при подключении:
1. я приготовил уже скомпилированные примеры.
2. при подключении каждый этап сопровождается комментарием. И в случае успеха и в случае ошибки будет напечатано сообщение в окне программы.
Таким образом вы будете знать на каком этапе произошёл сбой (надеюсь, что до этого вообще не дойдёт).

Вот, что мы увидим в окне нашей программы, если нет ошибок.

Если забудем скопировать файл dll_1.dll в папку нашего проекта или неправильно запишем его название, то увидим это.

Если библиотека успешно загружена, но мы ошиблись в названии функции: Hello_From_DLL , то увидим следующее.

Сегодня отличный день, и нужно многое успеть сделать завтра, так как наши горизонты планирования значительно расширились.
Вы можете разместить самые важные функции в собственной динамической библиотеке.
Всё это очень похоже на космический запуск и стыковку в космосе-памяти вашей программы с выбранной функцией.
Именно с помощью динамической библиотеки функции раскрывают весь свой потенциал.
Во всех остальных случаях старайтесь использовать inline вызовы.

Источник: a-1a.livejournal.com

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru