Освобождение памяти, выделенной в другой DLL

У меня есть 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 в векторе папок (когда папки выходят за пределы области действия в конце aFunction):

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 кажется довольно настойчивым, что это проблема.

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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