crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

4 страниц V  < 1 2 3 4 >  
Ответить в данную темуНачать новую тему
> Использование сторонних библиотек, Как использовать функции из .dll?
Iron Bug
  опции профиля:
сообщение 22.4.2010, 23:36
Сообщение #21


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


ну вот тебе кусок из реального проекта. весь не привожу - там дохрена и файлов, и вызовов. но не важно.
(тут libftdi - библиотека драйвера для одной микросхемы)
Раскрывающийся текст

#include <dlfcn.h>


using namespace std;

typedef void* dll_handle_t;

typedef int (*ftdi_init_func_t)(struct ftdi_context *); // определения типов для точек входа библиотеки
...........
typedef void (*ftdi_deinit_func_t)(struct ftdi_context *);
class ftdidev_c
{
    public:
        ftdidev_c();
        ~ftdidev_c();

    protected:
        static dll_handle_t _dll;
        struct ftdi_context _ftdic;

        ftdi_init_func_t            pinit;   //་ указатели на функции, к которым динамически линкуются входы библиотеки
.....................
        ftdi_deinit_func_t          pdeinit;
    public:
        bool all_func_ok();
        int init();  // обёртка для вызова указателя на функцию
.........
        int deinit();
};


dll_handle_t ftdidev_c::_dll = dlopen("libftdi.so",RTLD_LAZY); // открытие хэндла библиотеки (без загрузки)

ftdidev_c::ftdidev_c()
{
    if(_dll != NULL)
    {
        pinit = (ftdi_init_func_t)dlsym(_dll,"ftdi_init");  // динамическая линковка точек в библиотеке к определённым внутри класса указателям на функции
.................................
        pdeinit = (ftdi_deinit_func_t)dlsym(_dll,"ftdi_deinit");
    }
    if(pinit != NULL)
    {
        (*pinit)(&_ftdic);
    }
}

ftdidev_c::~ftdidev_c()
{
    if(pdeinit != NULL)
    {
        (*pdeinit)(&_ftdic);
    }
}

bool ftdidev_c::all_func_ok()
{
    if(pinit == NULL) return false;
........................
    if(pdeinit == NULL) return false;
    return true;
}

int ftdidev_c::init()  // реализация обёртки для вызова указателя на функцию
{а на работе у меня
    if(pinit != NULL)
    {
        return (*pinit)(&_ftdic);
    }
    return FTDI_ERR_DLL_FUNC_UNDEFINED;
}


int ftdidev_c::deinit()
{
    if(pdeinit != NULL)
    {
        (*pdeinit)(&_ftdic);
        return FTDI_FUNC_OK;
    }
    return FTDI_ERR_DLL_FUNC_UNDEFINED;
}


(тут много дополнительных мелких ухищрений: просто у меня уже есть стандартизированный подход к работе с разными библиотеками и мне так привычнее. но не суть.)
вот это компилится без единого упоминания .a файла. линкуется с dl (ну там ещё rt и pthread, но это не существенно для примера).
я и libftdi.а и libftdi.la убрала вообще из /usr/lib и больше их нет нигде и доступа к ним нет вообще - они под рута убраны. остались только libftdi.so файлы.
однако, компилится и рекомпилится с нуля - будь здоров! потому что для работы с .so нужны только имена точек входа и имя самой .so библиотеки.

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

Сообщение отредактировал Iron Bug - 22.4.2010, 23:43
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 23.4.2010, 6:29
Сообщение #22


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Мы говорим про венду! Посмотри внимательно, что какие файлы генерируются вместе с dll. dll нельзя слинковать с exe.

Сообщение отредактировал BRE - 23.4.2010, 6:30
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 23.4.2010, 8:27
Сообщение #23


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


Цитата(BRE @ 23.4.2010, 10:29) *
dll нельзя слинковать с exe
ты имеешь в виду статически?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 23.4.2010, 8:57
Сообщение #24


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Цитата(Litkevich Yuriy @ 23.4.2010, 9:27) *
ты имеешь в виду статически?

Назовем это динамической линковкой.
Например, так линкуются QtCore, QtGui, ...
Мы не пишем код, который загружает эти библиотеки и настраивает адреса функций. Все это делает загрузчик, и если одной из dll не будет найдено, то еще до входа в main мы получим сообщение от загрузчика, что данная программа не может быть выполнена.
В линукс линкер может работать напрямую с so-файлами, в венде же вместе с dll, генерируется небольшой файл lib (.a), который линкер использует при компоновке.

Другой путь использовать dll, это загрузка его руками в своем коде, резольвинг необходимых функций и их использование. Для этого способа, этот файл lib (.a) не нужен, и программа будет запущенна даже если его нет (загрузчик про него ничего не знает и соответственно искать не будет). Это вариант для плагинов.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 23.4.2010, 9:02
Сообщение #25


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


В винде не все линкеры умеют пользоваться символами напрямую из dll-ки.

Например MSVC не умел - сейчас не знаю.
Старый mingw не умел - сейчас научили. Кроме того он понимает импортные либки от MSVC.
Багланд - не помню, вроде умеет. :)

Причём обычно библиотеки импорта (т. е. либки, которые для линковки с dll-ками) действительно содержат только перечень экспортируемых символов, что делает их довольно бессмысленными и легко конвертируемыми между разными форматами. :)
Но несколько раз мне встречались импортные либки, которые содержали ещё что-то, т. е. конвертировать их не удавалось...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 23.4.2010, 11:56
Сообщение #26


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(BRE @ 23.4.2010, 9:29) *
Мы говорим про венду! Посмотри внимательно, что какие файлы генерируются вместе с dll. dll нельзя слинковать с exe.

..ять! я уже три раза сказала, что разницы нет. под вендой и линём это совершенно одинаковые схемы. это не линковка, а динамическая загрузка. разные вещи.
я десять лет пишу такие проги, а ты мне тут говоришь, что нельзя :) каждый день я загружаю десятки разных сторонних библиотек для железа и всё нормально. а ты сейчас доказываешь мне, что все десять лет у меня ничего не работало.
точно так же - LoadLibrary и вперёд с песнями. и не нужно там ничего больше, кроме загрузки и линковки входов.
а как ваша QT устроена - этого я не знаю и это частные проблемы этой библиотеки. скорее всего, и её можно также грузить. просто для упрощения придумали автозагрузку и какую-нибудь инициализацию в виде статически линкующегося к коду куска.
вот кусок из работающего проекта, который знать не знает про lib'ы, линкует только стандартные библиотеки и прекрасно живёт, подключаясь к Ftd2xx.dll:
Раскрывающийся текст
class cFtdiDrv
{
.................
    HMODULE     fthmodule;

    FT_STATUS   ftLoadDLL(void);  

    typedef FT_STATUS (WINAPI *PtrToOpen)(PVOID, FT_HANDLE *);  // определение типа функции (точки входа)
    PtrToOpen pFtOpen; // указатель на функцию для подключения входа

    FT_STATUS ftOpen(PVOID); // обёртка для вызова прилинкованного указателя
................
}

FT_STATUS  cFtdiDrv::ftLoadDLL()
{
    fthmodule = LoadLibrary("Ftd2xx.dll");                          // загрузка Dll по имени
    if(fthmodule == NULL)
    {
         return FT_INVALID_HANDLE;
    }

    pFtOpen = (PtrToOpen)GetProcAddress(fthmodule, "FT_Open");    // подключение указателя к входу библиотеки по имени функции
    if (pFtOpen == NULL)
    {
        return FT_INVALID_HANDLE;
    }
.....
}

FT_STATUS cFtdiDrv::ftOpen(PVOID pvDevice)
{
    if (!pFtOpen)
    {
        return FT_INVALID_HANDLE;
    }
    return (*pFtOpen)(pvDevice, &ftHandle ); // вызов прилинкованной функции через указатель
}

совершенно та же фигня. только dlopen заменён на LoadLibrary, а dlsym на GetProcAddress. ну выгрузка библиотеки в обоих примерах пропущена, но она такая же аналогичная.

Сообщение отредактировал Iron Bug - 23.4.2010, 11:59
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 23.4.2010, 12:21
Сообщение #27


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Iron Bug, ты все динамические библиотеки загружаешь руками с помощью LoadLibrary и получаешь адреса их функций? Или все таки есть такие, которые "кто-то" загружает за тебя (например msvcrt.dll)? А как происходит процесс линковки таких библиотек?

Раньше все, сейчас (как сообщих Tonal) некоторые, линкеры использовали для этого специальную библиотеку импорта... Ну вроде все это уже несколько раз описано выше, не буду повторяться. :)
То, что ты не замечаешь как происходит этот процесс заслуга компилятора. Кстати, каким пользуешься?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 23.4.2010, 13:52
Сообщение #28


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(BRE @ 23.4.2010, 15:21) *
Iron Bug, ты все динамические библиотеки загружаешь руками с помощью LoadLibrary и получаешь адреса их функций? Или все таки есть такие, которые "кто-то" загружает за тебя (например msvcrt.dll)? А как происходит процесс линковки таких библиотек?

msvcrt загружает только основной модуль. он прикрепляет к юзерскому коду заголовок для загрузчика и прописывает основную точку входа(но и это можно сделать вручную, если нужно). остальные загрузки по ходу работы - см. приведённый код. динамическая загрузка вызовом LoadLibrary(она вызывается из Kernel32.dll), по которому в системе происходит загрузка библиотеки в память и вызов внутреннего метода библиотеки init (под вендой название точно не помню, но если сам пишешь dll, которой нужна инициализация - то его надо писать вручную, а если нет - то компилятор по типу собираемого модуля dll сам его прилепит, сгенерировав по умолчанию). после вызова этой функции и линковки точек входа библиотека готова к работе. и неважно, каким компилятором она собрала. если имена точек входа указаны без декораций(а для этого прописывается файл экспорта при сборке библиотеки), то это работает. и даже если пересобрать библиотеку, добавить что-то, то ничего не изменится, пока указанные имена входов и их параметры остались теми же.
производители аппаратуры так и делают, чтобы расширять свои библиотеки для новых версий. потому что иначе после каждого апдейта зависимый код пришлось бы перекомпилять из-за линковки по номерам входов. а так, один раз написал, отправил юзеру и пускай он себе обновляет дрова - для модуля ничего не изменится, пока интерфейс совместим.

Сообщение отредактировал Iron Bug - 23.4.2010, 13:56
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 23.4.2010, 14:07
Сообщение #29


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Цитата(Iron Bug @ 23.4.2010, 14:52) *
и неважно, каким компилятором она собрала. если имена точек входа указаны без декораций(а для этого прописывается файл экспорта при сборке библиотеки), то это работает. и даже если пересобрать библиотеку, добавить что-то, то ничего не изменится, пока указанные имена входов и их параметры остались теми же.
производители аппаратуры так и делают, чтобы расширять свои библиотеки для новых версий. потому что иначе после каждого апдейта зависимый код пришлось бы перекомпилять из-за линковки по номерам входов. а так, один раз написал, отправил юзеру и пускай он себе обновляет дрова - для модуля ничего не изменится, пока интерфейс совместим.

Да это все понятно, никто и не говорит обратного. Это базовые понятия про разделяемые библиотеки...

Вот ключевой момент:
Цитата(Iron Bug @ 23.4.2010, 14:52) *
msvcrt загружает только основной модуль. он прикрепляет к юзерскому коду заголовок для загрузчика и прописывает основную точку входа(но и это можно сделать вручную, если нужно).

А кто прикрепляет к юзерскому коду заголовок (а точнее таблицу импорта)? И откуда эта таблица берется? :)
А делает это линкеры, и по крайней, мере большей их части нужна для этого библиотека импорта.

При старте процесса, еще до передачи управления в функцию _main, загрузчик загружает dll используя LoadLibrary, резолвит нужные функции и уже в памяти настраивает эти таблицы импорта для процесса.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 23.4.2010, 14:29
Сообщение #30


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(BRE @ 23.4.2010, 17:07) *
А кто прикрепляет к юзерскому коду заголовок (а точнее таблицу импорта)? И откуда эта таблица берется?

см. код. когда ты линкуешь метод, вызывая GetProcAddress - система просматривает внутреннюю таблицу экспорта dll и, если находит метод с запрашиваемым именем, то подгружает его. имя это ты можешь выбирать вообще динамически. то есть, ты можешь написать программу для выполнения любого метода любой библиотеки, задавая их через юзерский интерфейс. и никакие lib'ы и статическая информация тут не нужны. в этом случае на этапе компиляции и линковки никаких предположений об адресе функции не делается вообще.
я знаю, что именно ты путаешь с динамической загрузкой. есть т.н. динамическая линковка. но это линковка по номерам входов. загрузка библиотеки происходит при загрузке основного модуля (msvcrt генерит автоматический код) и в модуле буквально прописываются связи типа "вызов метода номер 2 из библиотеки такой-то". и вот именно для этого и нужны эти самые таблицы импорта. но это не универсальный метод, в том смысле, что при существенных изменениях в библиотеке номера входов могут меняться и тогда кердык всем зависящим от этих номеров приложениям. при этом при смещении номеров происходит вызов не той функции (причём система не проверяет этого) и программа чаще всего просто падает с access voilation. мелкософт обозвали это явление hell dll и начали городить огород с системой хранения версий в весьма дурацком стиле. а в линюксе для этого изначально были методы (приписывание версии к названию .so библиотеки).
кроме этого, есть ещё гарантированно работающая статическая линковка, понятное дело.

так что таблицы экспорта - это полумера и она работает в весьма ограниченном диапазоне, хотя и куда легче в реализации, потому что компилятор сам всё делает за программиста.

Сообщение отредактировал Iron Bug - 23.4.2010, 14:29
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

4 страниц V  < 1 2 3 4 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 23.11.2024, 3:58