Не все символы экспортированного в DLL класса экспортируются (VS9) - PullRequest
0 голосов
/ 10 июня 2010

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

Я делаю объявление всех символов, с которыми я хочу экспортироватьопределение препроцессора, например:

#if defined(MYPROJ_BUILD_DLL)
//Build as a DLL
#   define MY_API __declspec(dllexport)
#elif defined(MYPROJ_USE_DLL)
//Use as a DLL
#   define MY_API __declspec(dllimport)
#else
//Build or use as a static lib
#   define MY_API
#endif

Например:

class MY_API Foo{ 
   ...
}

Затем я создаю статическую библиотеку с неопределенным MYPROJ_BUILD_DLL & MYPROJ_USE_DLL, вызывающим создание статической библиотеки.

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

Редактировать: Примечание по неподтвержденным символам: параметры компоновщика Установлены для хранения данных без ссылок (/OPT:NOREF) и Не удалять избыточные КОМДАТЫ (/OPT:NOICF), так что никакие символы без ссылок не будут удалены.

Хорошо, теперьк проблеме.Когда я использую эту новую DLL, я получаю неразрешенные внешние данные, потому что экспортируются не все символы класса.Например, в таком классе:

class MY_API Foo{
public:
   Foo(char const* );
   int bar();
private:
   Foo( char const*, char const* );
};

Только Foo::Foo( char const*, char const*); и int Foo::bar(); экспортируется.Как это может быть?Я могу понять, отсутствовал ли весь класс, например, из-за того, что я забыл включить заголовок в сборку DLL.Но это только частично отсутствует.

Также, скажем, если Foo::Foo( char const*) не было реализовано;тогда сборка DLL будет иметь неразрешенные внешние ошибки.Но сборка в порядке (я также дважды проверил объявления без реализации).

Примечание: Совокупный размер статических библиотек, которые я объединяю, составляет около 30 МБ, аПолученная DLL имеет размер 1,2 МБ.

Я использую Visual Studio 9.0 (2008) для сборки всего.И Зависит для проверки экспортируемых символов.

Редактировать: Для тех, кто задается вопросом, почему я не просто собираю DLL из каждой статической библиотеки: Iне могу, потому что они перекрестно ссылаются друг на друга (поэтому мне нужно сгруппировать их в одну DLL).Я знаю, это ужасно, я не могу понять логику этого.

Ответы [ 3 ]

3 голосов
/ 10 июня 2010

Помните, что когда вы ссылаетесь на статическую LIB, по умолчанию компоновщик извлекает только те функции, классы и данные, на которые фактически ссылается клиент (который в данном случае является вашей DLL ).

Итак, что происходит, это:

  1. Вы строите свою статическую LIB, и это нормально. Эта LIB действительна на 100%.
  2. Теперь вы строите свою статическую DLL на основе исходной двоичной LIB. Он извлекает только то, на что ссылается . Он не включает в себя полное определение класса , которое ему необходимо сделать. Эта библиотека DLL, хотя и создает, является недействительной.
  3. Затем клиент использует DLL и ожидает увидеть полное двоичное определение для экспортируемого класса, потому что это то, что клиент видит в прилагаемом заголовочном файле.
  4. Однако класс был только частично импортирован, и именно поэтому вы получаете эти ошибки ссылки.

Исправить:

  • Если вы можете, не создавайте свою DLL из вашей статической LIB. Создайте свою DLL из оригинального исходного кода. И создайте свою статическую LIB из исходного исходного кода.
  • В противном случае вы можете поиграть с настройками компоновщика, но я настоятельно рекомендую вариант 1 выше. Обратите внимание, что OPT: NOREF здесь не будет работать, так как OPT: NOREF не влияет на статические LIB.

Что не исправит:

  • Высокоуровневый компоновщик настраивает, например, OPT: NOREF, все, что связано с COMDAT и т. Д. Если вы хотите, чтобы эти функции присутствовали, вы должны убедиться, что на них ссылаются, либо ссылаясь на них, либо явно сообщая компоновщику: «эй на этот Символ X имеется ссылка ".

В качестве примечания:

  • Каждый раз, когда я собираю DLL или LIB, я создаю и DLL и LIB, используя именно ту методику, которую вы используете. Идеальным является наличие единой базы кода, которая может генерировать либо DLL, либо LIB путем переключения настроек. Но построение DLL из (двоичной) статической LIB, когда у вас есть исходный код для обоих ... Мне трудно представить, когда такой сценарий будет необходим.
2 голосов
/ 10 июня 2010

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

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

1 голос
/ 16 февраля 2016

Вероятно, это на несколько лет позже, но OP жалуется на то, что закрытый метод (деструктор) не был экспортирован из класса dllexport d. Разве это не нормально? Никакой внешний пользователь не может вызывать закрытый метод.

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