Неуправляемые библиотеки DLL в C ++ - PullRequest
8 голосов
/ 25 октября 2008

Я читал много учебников / статей по неуправляемым DLL на C ++. Однако в жизни я не могу понять концепцию. Меня легко смущает кажущееся несогласие относительно того, нужен ли ему файл заголовка, как его экспортировать, нужен ли мне файл .lib и что у вас.

Итак, давайте предположим, что у меня есть такая функция:

public int calculateSquare(int num)
{
    return num*num;
}

Игнорируя реальный код, что мне нужно, чтобы эта простая функция сама по себе превратилась в DLL, которую я затем могу вызвать? Я просто добавляю __dllexport или что-то еще в первую строку или мне нужен заголовок? Я озадачен всем этим.

Ответы [ 4 ]

15 голосов
/ 25 октября 2008

Я не могу не подчеркнуть этого достаточно, компилятор C ++ не видит заголовочные файлы, после того, как препроцессор сделан, есть только один большой исходный файл (также называемый модулем компиляции). Так что строго вам не нужен заголовок для экспорта этой функции из DLL. Что вам нужно, так это какая-то форма условной компиляции, чтобы экспортировать функцию в dll, которую вы компилируете, и импортировать ее в код клиента.

Обычно это делается с помощью комбинации макросов и заголовочных файлов. Вы создаете макрос с именем MYIMPORTEXPORT и с помощью условных операторов макросов заставляете его работать как __declspec (dllexport) в dll и __declspec (dllimport) в клиентском коде.

в файле MYIMPORTEXPORT.h

#ifdef SOME_CONDITION
#define MYIMPORTEXPORT __declspec( dllexport )
#else
#define MYIMPORTEXPORT __declspec( dllimport )
#endif

в файле MyHeader.h

#include <MyImportExport.h>

MYIMPORTEXPORT public int calculateSquare(int num)
{
    return num*num;
}

в файле dll .cpp

#define SOME_CONDITION

#include <MyHeader.h>

в коде клиента .cpp файл

#include <MyHeader.h>

Конечно, вам также нужно сообщить компоновщику, что вы создаете dll с опцией / DLL .

Процесс сборки также создаст файл .lib, это статическая библиотека, в данном случае называемая заглушкой, на которую клиентский код должен ссылаться, как если бы он ссылался на настоящую статическую библиотеку. Автоматически, DLL будет загружен при запуске клиентского кода. Конечно, dll должна быть найдена ОС через механизм поиска, что означает, что вы не можете поместить dll просто где-нибудь, но в определенном месте. Здесь подробнее об этом.

Очень удобный инструмент для просмотра того, экспортировали ли вы правильную функцию из dll и правильно ли импортирует код клиента: dumpbin . Запустите его с / EXPORTS и / IMPORTS соответственно.

6 голосов
/ 25 октября 2008

QBziZ 'ответ достаточно правильный. См. Неуправляемые библиотеки DLL в C ++

Чтобы завершить это: В C ++, если вам нужно использовать символ, вы должны сообщить компилятору о его существовании, а часто и его прототипе .

На других языках компилятор просто исследует библиотеку самостоятельно и найдет символ et voilà .

В C ++ вы должны сообщить компилятору.

См. Заголовок C / C ++ как оглавление книги

Лучший способ - поместить в какое-то общее место необходимый код. «Интерфейс», если хотите. Обычно это делается в заголовочном файле, который называется header, потому что обычно это не независимый исходный файл. Заголовок - это всего лишь файл, целью которого является включение (т.е. копирование / вставка препроцессором) в настоящие исходные файлы.

По сути, кажется, что вам нужно дважды объявить символ (функцию, класс, что угодно). Это почти ересь по сравнению с другими языками.

Вы должны увидеть это как книгу, со сводной таблицей или указателем. В таблице у вас есть все главы. В тексте у вас есть главы и их содержание.

А иногда вы просто счастливы, что у вас есть список глав.

В C ++ это заголовок.

А как насчет DLL?

Итак, вернемся к проблеме с DLL: цель DLL - экспортировать символы, которые будет использовать ваш код.

Таким образом, на языке C ++ вы должны одновременно экспортировать код при компиляции (т. Е. В Windows, например, использовать __declspec) и «опубликовать» таблицу экспортируемого (т. Е. Иметь «публичные» заголовки, содержащие экспортируемые декларации).

1 голос
/ 25 октября 2008

Контрольный список для экспорта функций:

  • Подходит ли соглашение о вызовах для вызывающего абонента? (это определяет, как параметры и результаты передаются, и кто отвечает за очистку стека). Вы должны четко указать свое соглашение о вызовах.
  • Под каким именем символ будет экспортироваться? C ++ обычно должен украшать ("калечить") имена символов, например, различать разные перегрузки.
  • Сообщите компоновщику сделать функцию видимой как DLL Export

На MSVC:

  • __stdcall (это Паскальское соглашение о вызовах) - это типичное соглашение о вызовах для экспортируемых символов, которое поддерживается большинством клиентов.
  • extern "C" позволяет экспортировать символ C-стиля без искажения имени
  • используйте __declspec(dllexport), чтобы пометить экспортируемый символ, или связать отдельный файл .def, в котором перечислены экспортируемые символы. С помощью файла .def вы также можете экспортировать только по порядковому номеру (не по имени) и изменить имя экспортируемого символа.
0 голосов
/ 25 октября 2008

Вам необходимо экспортировать функцию, используя либо __declspec( dllexport ), либо добавив функцию в файл определения модуля (.def). Затем скомпилируйте проект как DLL.

На стороне клиента у вас есть два варианта. Либо используйте библиотеку импорта (.lib), которая создается при компиляции DLL. Простое соединение с вашим клиентским проектом с этой библиотекой даст вам доступ к функциям, экспортированным из DLL. И вам нужен файл заголовка, потому что компилятор должен знать сигнатуру вашей функции - что он возвращает int и принимает int. Напомним, что вам нужно связать библиотеку импорта (.lib) и файл заголовка, который содержит заголовок вашей функции.

Другим способом является динамическая загрузка DLL с использованием WinAPI, вызова LoadLibrary и затем GetProcAddress для получения указателя на функцию. Указатель на функцию должен иметь правильный тип, чтобы компилятор мог дать ему правильные параметры и использовать правильное соглашение о вызовах.

...