Подтвердить что ты не робот

Почему происходит сбой этой программы: передача std::string между DLL

У меня есть некоторые проблемы с выяснением причин следующих сбоев (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?

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

4b9b3361

Ответ 1

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

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

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

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

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


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

Ответ 2

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

Да.

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

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

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

Ответ 3

В дополнение к тому, что было сказано выше, убедитесь, что Platform Toolset (в разделе "Свойства- > Общие" ) идентичен в обоих проектах. В противном случае содержимое строки на прибывающей стороне может быть фиктивным.

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

Ответ 4

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

Ответ 5

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

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

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

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

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