Освобождение памяти, выделенной в другой DLL - PullRequest
9 голосов
/ 28 октября 2009

У меня есть файл EXE, использующий файл DLL, который использует другой файл DLL. Возникла такая ситуация:

В файле DLL 1:

class abc
{
    static bool FindSubFolders(const std::string & sFolderToCheck, 
                               std::vector< std::string > & vecSubFoldersFound);
}

В файле DLL 2:

void aFunction()
{
    std::vector<std::string> folders;
    std::string sLocation;
    ...
    abc::FindSubFolders(sLocation, folders)
}

В режиме релиза все работает нормально. Но в режиме отладки у меня возникает ошибка утверждения в деструкторе одного из std::strings в векторе папок (когда папки выходят из области видимости в конце функции):

dbgheap.c : line 1274

/*
 * If this ASSERT fails, a bad pointer has been passed in. It may be
 * totally bogus, or it may have been allocated from another heap.
 * The pointer MUST come from the 'local' heap.
 */
_ASSERTE(_CrtIsValidHeapPointer(pUserData));

Я предполагаю, что это потому, что память была выделена в куче DLL-файла 1, но освобождается в DLL-файле 2.

Комментарий в dbgheap.c кажется довольно настойчивым, что это проблема.

Почему это такая проблема, когда кажется, что она работает нормально, если я просто игнорирую это? Есть ли способ сделать это без ошибок?

Ответы [ 4 ]

11 голосов
/ 28 октября 2009

Как уже говорил Шон, при выпуске сборки просто игнорируется этот оператор удаления, поэтому лучшее, на что вы можете надеяться, это утечка памяти.

Если вы контролируете, как компилируются оба DLL-файла, обязательно используйте параметры многопоточной отладочной DLL (/ MDd) или многопоточной DLL (/ MD) для библиотеки времени выполнения. Таким образом, оба DLL-файла будут использовать одну и ту же систему времени выполнения и использовать одну и ту же кучу.

Недостатком является то, что вам необходимо установить систему выполнения вместе с вашим приложением (для этого Microsoft предлагает установщик). Он будет отлично работать на вашей машине для разработки, поскольку Visual Studio также установит эту систему времени выполнения, но на только что установленной машине она сообщит об отсутствующих файлах DLL.

7 голосов
/ 28 октября 2009

Скорее всего, выпуск релиза имеет ту же проблему, но релиз релизов не утверждается. Они просто игнорируют проблему. Вы можете никогда не увидеть проблему. Или вы можете увидеть повреждение данных. Или вы можете увидеть сбой. Возможно, только ваши пользователи будут иметь ошибки, которые вы просто не сможете воспроизвести.

Не игнорируйте утверждения CRT.

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

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

6 голосов
/ 27 ноября 2012

Как говорят другие, проблему можно решить, убедившись, что ЭЛТ совместно используется двумя модулями. Но есть распространенные сценарии, в которых этот контракт трудно реализовать.

Причина заключается в том, что проверка связи с общим CRT не будет работать, если EXE и DLL не будут ссылаться на одну и ту же CRT версию (как в 6.0, 7.0, 8.0). Например, если вы возьмете DLL, встроенную в VC6.0, и попытаетесь использовать ее со сборкой EXE в VS2010, вы получите ту же проблему, что и раньше. Две версии CRT будут загружены в ваш процесс, и каждая из них будет использовать свою собственную кучу для распределения, независимо от того, будут ли ваши EXE и DLL использовать «общую» CRT, они не будут одинаковыми.

Лучшей практикой для API было бы убедиться, что объекты, размещенные на одной стороне, также уничтожаются на той же стороне. Я знаю, это звучит некрасиво, но это единственный способ убедиться, что DLL остается бинарно-совместимой.

5 голосов
/ 28 октября 2009

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

В настоящее время это стандартная практика для приложения, и все файлы DLL должны быть собраны с использованием динамической версии стандартной библиотеки.

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

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