.def файлы C / C ++ DLLs - PullRequest
       66

.def файлы C / C ++ DLLs

22 голосов
/ 14 декабря 2008

Я не понимаю смысла использования файлов .def с DLL.

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

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

Ответы [ 6 ]

24 голосов
/ 28 февраля 2009

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

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

Однако добавление в сборку файла .def, в котором перечислены все ваши экспортируемые функции, позволяет запретить компиляторам Microsoft (например) добавлять начальную подчеркивание и конечную информацию о ширине параметра к имени экспортируемой функции (по крайней мере, при объединении). с директивой __stdcall, также полезной для переносимости). Например. объявление функции

void foo(int i);

может закончиться экспортом как "_foo @ 4", если вы не будете осторожны с соглашением о вызовах и использованием файла .def.

Сохранение экспортированных имен функций в таблице символов без таких декораций имен становится очень удобным при выполнении вызовов GetProcAddress () как части загрузки и явного подключения к DLL во время выполнения. т.е. чтобы получить указатель на указанную выше функцию foo () (при условии, что она вообще была экспортирована) во время выполнения, в идеале вы просто хотите вызвать:

HANDLE dllHandle = LoadLibrary("mydll.dll");
void* fooFcnPtr = GetProcAddress(dllHandle, "foo");

С некоторой соответствующей проверкой ошибок, конечно!

Использование файла .def плюс __stdcall, __declspec (dllexport) и extern "C" в ваших объявлениях функций при сборке DLL обеспечит работу вышеуказанного клиентского кода для широкого диапазона компиляторов и настроек компилятора.

20 голосов
/ 14 декабря 2008

Насколько я понимаю, файлы .def предоставляют альтернативу синтаксису __declspec (dllexport), с дополнительным преимуществом возможности явно указывать порядковые номера экспортируемых функций. Это может быть полезно, если вы экспортируете некоторые функции только по порядковому номеру, который не раскрывает столько информации о самой функции (например, многие из функций экспорта внутренней библиотеки ОС только по порядковому номеру).

См. Справочную страницу .

Обратите внимание, что имена в файле .def должны совпадать с именами в двоичном файле. Поэтому, если вы используете C или C ++ с 'extern "C" {...}', имена не будут искажены; в противном случае вы должны использовать правильные искаженные имена для конкретной версии компилятора, используемой для генерации DLL. Функция __declspec () делает все это автоматически.

8 голосов
/ 18 декабря 2008

Для тех, кто еще заинтересован ... чтобы иметь возможность ссылаться на файл dll и def, вам также потребуется файл lib. В окнах это можно сделать из определения с помощью инструмента «LIB». Ниже приведен пример того, как это сделать из командной строки.

lib /machine:i386 /def:sqlite3.def

Надеюсь, это поможет другим.

4 голосов
/ 14 декабря 2008

.DEF файлы чаще встречались в 16-битных окнах, где они обычно были единственным способом указать, какие символы должны быть экспортированы.

Кроме того, они предоставили средства для определения экспорта по порядковому значению (@ 1, @ 2 и т. Д.), А не по имени. Этот метод поиска символов использовался, когда производительность была действительно важной, например, в видеодрайверах.

3 голосов
/ 14 декабря 2008

Насколько я понимаю, файлы .def на самом деле не указывают, какие все apis нужно экспортировать. Он содержит только экспортированные apis и их порядковые номера. Если вы хотите экспортировать конкретный API-интерфейс, вам необходимо указать __declspec (dllexport) в определении API-интерфейса и __declspec (dllimport) в объявлении.

Преимущество файла def заключается в том, что он помогает поддерживать совместимость обратного слова с уже реализованными dll. то есть он поддерживает порядковые номера для apis. Предположим, что вы добавляете новый API в DLL, затем компоновщик просматривает ваш файл .def, генерируя порядковый номер для ne wapi, так что порядковые номера для старого API сохраняются.

Таким образом, если код клиента использует последнюю версию DLL, он не нарушает существующий API.

3 голосов
/ 14 декабря 2008

Я мало работал с DLL, но, насколько я понимаю, для экспортированных функций C ++ вы должны использовать «__declspec (dllexport)», а для экспортированных функций C вы должны написать файл .def. Вероятно, это связано с тем, что функции C ++ поддерживают перегрузку, а функции C - нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...