Почему делает этот катастрофический отказ программы: передача станд.:: строка между DLLs

Я испытываю некоторые затруднения при выяснении почему следующие катастрофические отказы (MSVC9):

//// the following compiles to A.dll with release runtime linked dynamically
//A.h
class A {
  __declspec(dllexport) std::string getString();
};
//A.cpp
#include "A.h"
std::string A::getString() {
   return "I am a string.";
}

//// the following compiles to main.exe with debug runtime linked dynamically
#include "A.h"
int main() {
   A a;
   std::string s = a.getString();
   return 0;
} // crash on exit

Очевидно(?), это происходит из-за различных моделей памяти для исполняемого файла и DLL. Могло случиться так что строка A::getString() возвраты выделяются в A.dll и освобождаются в main.exe?

Если так, почему - и каков был бы безопасный способ передать строки между DLLs (или исполняемые файлы, в этом отношении)? Не используя обертки как shared_ptr с пользовательским средством удаления.

25
задан Macke 9 April 2010 в 15:22
поделиться

4 ответа

На самом деле это не вызвано разными реализациями кучи - реализация MSVC std :: string не использует динамически выделяемую память для таких маленьких строк (это использует оптимизацию малых строк). ЭЛТ действительно должны совпадать, но на этот раз вас укусило не это.

Происходит то, что вы вызываете неопределенное поведение, нарушая правило одного определения .

В сборках выпуска и отладки будут установлены разные флаги препроцессора, и вы обнаружите, что std :: string имеет разные определения в каждом случае. Спросите у своего компилятора, что такое sizeof (std :: string) - MSVC10 сообщает мне, что его 32 в отладочной сборке и 28 в сборке выпуска (это не заполнение - 28 и 32 - оба по 4 байта` границы).

Так что происходит? Переменная s инициализируется с использованием отладочной версии конструктора копирования для копирования версии выпуска std :: string . Смещения переменных-членов в разных версиях различаются, поэтому вы копируете мусор. Реализация MSVC эффективно хранит указатели начала и конца - вы скопировали в них мусор; поскольку они больше не равны нулю, деструктор пытается освободить их, и вы получаете нарушение прав доступа.

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


В итоге: версии CRT должны соответствовать, но должны соответствовать определениям, включая определения в стандартной библиотеке .

52
ответ дан 28 November 2019 в 18:27
поделиться

Может быть, строка, возвращаемая A :: getString (), выделяется в A.dll и освобождается в main.exe?

Да.

Если да, то почему - и что было бы безопасным способом передачи строк между библиотеками DLL (или исполняемыми файлами, если на то пошло)? Без использования оболочек, таких как shared_ptr, с пользовательским средством удаления .

Использование shared_ptr кажется мне разумным. Помните, что, как показывает практика, выделения и освобождения должны выполняться одним и тем же модулем, чтобы избежать подобных сбоев.

Экспорт объектов STL в библиотеки DLL - в лучшем случае непростая задача. Я предлагаю вам сначала ознакомиться с этой статьей базы знаний MSDN и этой статьей .

3
ответ дан 28 November 2019 в 18:27
поделиться

Вам необходимо связать одну и ту же библиотеку времени выполнения (библиотеку DLL), отладочную или выпускаемую, для каждой библиотеки DLL в вашем приложении, где память выделяется в одной и освободили в другом. (Причина использования динамически подключаемой библиотеки времени выполнения заключается в том, что тогда будет одна куча для всего вашего процесса, а не одна куча для каждой dll / exe, которая ссылается на статическую.)

Это включает возврат std :: string и stl-контейнеры по значению, так как это то, что вы делаете.

Причины двоякие (обновленный раздел) :

  • классы имеют разные макеты / размеры, поэтому по-разному скомпилированный код предполагает, что данные находятся в разных местах.Кто бы ни создал его первым, будет прав, но другой рано или поздно вызовет сбой.
  • реализации кучи msvc различны в каждой runtime-lib, что означает, что если вы попытаетесь освободить указатель в куче, который не выделял его, он сойдет с ума. (Это происходит, если макеты похожи, т.е. когда вы переживаете первый случай.)

Итак, получите ваши библиотеки времени выполнения прямо или прекратите освобождение / выделение в разных dll (т.е. прекратите передавать данные по значению).

2
ответ дан 28 November 2019 в 18:27
поделиться

Это может быть связано с тем, что DLL и EXE скомпилированы с разными настройками CRT. Поэтому, когда вы передаете строку, возникает конфликт ресурсов. Проверьте настройки вашего проекта как для библиотеки DLL, так и для исполняемого файла.

1
ответ дан 28 November 2019 в 18:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: