Как мне создать независимую от версии библиотеку DLL на C ++? - PullRequest
12 голосов
/ 01 июня 2011

Мой продукт представляет собой библиотеку C ++, которая в Windows распространяется как DLL.Он очень мало использует c-runtime (базовый iostream и все), поэтому я уверен, что все последние версии CRT будут в порядке.

Поскольку мой клиент должен строить свое приложение, используямой dll, я не хочу навязывать ему какую-либо конкретную версию во время выполнения.Я бы хотел, чтобы мой dll связывался с любой версией библиотеки времени выполнения, используемой моим клиентским приложением (и я могу предположить, что он будет использовать динамическое связывание для своего CRT).В конце концов, разве это не динамическое связывание?Возможно ли это?

РЕДАКТИРОВАТЬ: связывание DLL со статическими библиотеками времени выполнения также не будет работать, потому что тогда статическая среда выполнения (из библиотеки DLL) и динамическая среда выполнения (из приложения клиента) будут смешаны,что плохо.

РЕДАКТИРОВАТЬ: я в основном спрашиваю, как мне сказать загрузчику времени выполнения связать мою dll с любым CRT, с которым связано приложение?Возможно, что-то с манифестом?В более общем плане, мой вопрос заключается в том, как создать dll с хорошим поведением, который будет использоваться клиентами, создающими свои собственные приложения?

РЕДАКТИРОВАТЬ: Благодаря советам в ответах я перенес все ссылкиSTD классы в встроенные функции в моих заголовках, и связал мою DLL со статическими библиотеками времени выполнения.Теперь он работает даже в приложениях, связанных с различными версиями CRT.

Ответы [ 8 ]

12 голосов
/ 01 июня 2011

Нет реального способа гарантировать, что ваша DLL работает с несколькими средами выполнения - любой из типов, которые изменяются между ними, может привести к несовместимости.Например, размер объекта может измениться, или расположение членов в них.В С ++ очень мало места для такого рода вещей.

Лучшее, что вы можете сделать, это статически связать со средой выполнения и убедиться, что экспортируемый API ограничен типами, находящимися строго под вашим контролем - нет передачи std::string для функции, нет stdlib типов в качестве членов, и не new в одной DLL и delete в другой.Не смешивайте встроенные и экспортированные функции (включая конструкторы / деструкторы) для одного и того же объекта, потому что порядок членов и отступы могут изменяться между компиляторами.Здесь может помочь идиома pimpl.

6 голосов
/ 01 июня 2011

Если вы предоставляете какие-либо объекты C ++ через границы DLL, это просто невозможно. Что вы можете сделать (и мы используем стороннюю DLL, которая делает это), это собрать вашу библиотеку в нескольких конфигурациях (32-битная / 64-битная, отладка / выпуск, статическая / динамическая среда выполнения, статическая / динамическая библиотека), чтобы удовлетворить как как можно больше людей. Вначале это может быть немного утомительно для настройки, но как только вы настроите все конфигурации, это просто вопрос их сборки. Конечно, вам также необходимо учитывать, какую среду выполнения вы строите (vc8, vc9, vc10 и т. Д.), Поэтому, если вы хотите охватить все базы, у вас может быть достаточно много конфигураций.

4 голосов
/ 01 июня 2011

Связывание вашей DLL со статическими библиотеками времени выполнения должно работать, за исключением того, что вы должны быть очень осторожны с управлением памятью (например, тот, кто вызывает вашу DLL, не может освободить () или удалить [] что-либо, выделенное вашей DLL), и вы не можете обмениватьсястандартные структуры данных C (например, FILE *).(Я что-то упустил?)

3 голосов
/ 01 июня 2011

Если вы хотите выставлять свои объекты нейтральным образом во время выполнения, тогда я не вижу другого решения, кроме COM.

3 голосов
/ 01 июня 2011

Этого можно добиться, используя вызовы WinAPI для ввода-вывода и все остальное, что, возможно, зависит от времени выполнения.

Самым болезненным является то, что вам, возможно, придется переопределить глобальные new и delete, чтобы использовать функции WinAPI исключительно потому, что они могут использовать malloc / free внутри.В этом есть много других болезненных аспектов, и я считаю, что это не стоит проблем. Здесь - статья, освещающая эту тему.

2 голосов
/ 02 июня 2011

Ну, есть огромная разница между C-runtime и C ++ - runtime. Если вам нужно использовать msvcrt.dll, которая в последние годы стала «рыцарской» как истинная системная DLL, вы можете рассчитывать на ее существование в XP и более поздних версиях (хотя для Windows 2000 вам понадобится распространяемый пакет для версии 6 msvcrt.dll). ). Вы можете использовать msvcrt.dll, скомпилировав код с компилятором из последних WDK (Windows Driver Kits). Несмотря на то, что это код пользовательского режима, он является жизнеспособным и хорошим методом для его компиляции.

Однако для IOStreams требуется среда выполнения C ++. И это многое усложняет.

РЕДАКТИРОВАТЬ: связывание DLL против статические библиотеки времени выполнения тоже не будут работать, потому что тогда статическая среда выполнения (от DLL) и динамическое время выполнения (от приложение клиента) будет смешанный, что плохо.

Что ж, если вы смешаете код таким образом, у вас что-то не так в вашем дизайне. Подобные проблемы могут возникнуть при запуске отладочной сборки вашей DLL с выпуском сборки другого кода или наоборот.

Я могу только порекомендовать вам либо напрямую использовать COM, либо - если он слишком большой - попробовать подражать некоторым идеям COM. Наиболее важным из них будет то, что у вас есть фабричная функция и что (класс) интерфейс объявлен (и никогда не изменяется) между этими двумя частями кода (т. Е. DLL и ее вызывающей стороной). Функция фабрики будет возвращать экземпляр класса, а сам класс будет управлять своим временем жизни (что подразумевает, что весь код для размещения и освобождения будет находиться в одной и той же сущности, то есть в вашей DLL). Управление сроком службы будет затем предоставлено через функции-члены addref и release. IUnknown может быть основой для вашего интерфейса, не полагаясь на другие части фактического COM.

РЕДАКТИРОВАТЬ: я в основном спрашиваю, как сделать Я говорю загрузчику, чтобы связать мой длл против любого ЭЛТ приложение связано с? Что-то с манифестом, возможно? Больше вообще мой вопрос как построить хорошо ведущий себя dll, это должно быть используется клиентами, строящими они сами приложения?

Не просто. Даже если бы у вас были установлены все версии VS, вам нужно было бы выйти из этой дилеммы, чтобы выбрать правильную версию.

0 голосов
/ 04 декабря 2012

Если вы используете C ++, кажется, что невозможно пересечь границы времени выполнения, если вы не ограничиваете себя тем, что может быть выставлено.Как упоминалось ранее, std :: objects не работают (например, std :: string).

Вот небольшой пример, который вызовет сбой:

class Base
{
public:
virtual ~ Base ()
{
}
};

class ClassInDll: public Base
{public: __declspec (dllexport) ClassInDll (int arg);
__declspec (dllexport) ~ ClassInDll ();

private: int _arg;
};

Если этот класс скомпилирован в DLL-файл режима выпуска VS2008, и в режиме отладки VS2008 создается файл .exe, выполняя следующие действия:

ClassInDll * c = new ClassInDll (1);delete c;

оператор "delete c" вызывает сбой.Это связано с тем, что ClassInDll имеет виртуальный деструктор.

0 голосов
/ 01 июня 2011

Ваша dll связана с c-runtime, с которым она была скомпилирована. Ваше приложение всегда будет использовать эту среду выполнения. Любой, кто ссылается на вашу dll, использует их c-runtime. Так что с этим проблем не будет.

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