C ++: проблема с выгрузкой Dll - PullRequest
5 голосов
/ 20 января 2009

Как я могу убедиться, что dll не выгружена, когда в ней есть какие-либо объекты?

Проблема в том, что когда я использовал явное управление памятью, я мог удалить объекты dll перед освобождением dll, однако с помощью умных указателей я не имею никакого контроля над порядком уничтожения, то есть dll может быть освобожден первым, вызывая сбой, когда пытаясь освободить один из других объектов:

FlPtr - это простой класс подсчета ссылок, который при необходимости вызывает AddRef и Release

ExampleDll *dll = LoadDll(L"bin\\example.dll");
IObject *obj = dll->CreateObject();
...
obj->Release();
delete dll;//fine because all objects already deleted
return 0;

auto_ptr<ExampleDll> dll = LoadDll(L"bin\\example.dll");
FlPtr<IObject> obj = dll->CreateObject();
...
return 0;//crash if dll is destructed before obj since Object::Release needs to call into the dll

Я пытался заставить dll-дескриптор выгружаться сам, то есть выгружаться только после удаления всех объектов. Эта работа путем создания нового объекта IExampleDll, который реализует DLL. Это похоже на объект ExampleDll из предыдущего, но живет в dll, а не в exe, и также считается ссылкой. Каждый объект в dll увеличивает это значение при конструировании и увеличивает его при разрушении. Это означает, что счетчик ссылок достигает нуля только тогда, когда exe выпустил свои ссылки и все объекты dll были уничтожены. Затем он удаляет себя, вызывая FreeLibrary (GetModuleHandle ()) в своем деструкторе.

Это, однако, происходит сбой во FreeLibrary, я предполагаю, что поток все еще находится в выгружаемом коде dll ...

Теперь я в растерянности, как убедиться, что dll выгружается только тогда, когда нет оставшихся объектов, кроме как вернуться к освобождению dll явно после того, как все остальное должно было быть удалено;

int main()
{
    ExampleDll *dll = LoadDll("bin\\example.dll");
    restOfProgram();
    delete dll;
}

Этот подход становится трудным, когда библиотеки dll должны загружаться / выгружаться в середине программы, т. Е. Если пользователь перешел с d3d на openGL в опциях.

Ответы [ 4 ]

6 голосов
/ 20 января 2009

Предполагая, что вы не хотите прерывать поток при выгрузке библиотеки (в противном случае см. MSalters), вам необходимо освободить библиотеку от вызывающей программы, которая ее загрузила.

COM решает эту проблему с помощью счетчика экземпляров в DLL (очень похоже на ваш, если я вас правильно понимаю) и регулярно проверяет его, вызывая глобально экспортированную CanUnloadNow функцию.

Другой вариант - использовать умные указатели вашего объекта / интерфейса ТАКЖЕ ссылаться на DLL, из которой они получены. Это увеличит размер данных клиента, но вам не нужно прикасаться к DLL. Вы можете даже использовать счетчик ссылок LoadLibrary / FreeLibrary, однако это может привести к снижению производительности.

Кроме того, ни одна из этих схем не очень поможет, если вы получаете циклические зависимости DLL (компонент DllA.X ссылается на DllB.Y, который ссылается на DllA.Z). У меня пока нет хорошего решения, которое не требует глобальных знаний.

1 голос
/ 20 января 2009

В случае, когда DLL переключается во время выполнения, я бы избегал системы интеллектуальных указателей для объектов, созданных DLL, и использовал бы такую ​​систему:

                    |-----------------------|  |--------------------------|
                    | Abstraction Interface |  | Implementation Interface |
                    |-----------------------|  |--------------------------|
                               ^                           ^
                               |                           |
|-------------|1     *|-------------------|*      *|----------------|
| Application |-------| Abstraction Layer |--------| Implementation |
|-------------|       |-------------------|        |----------------|

\------------- Main Program ------------------/ \-------- DLL --------/

Приложение содержит список всех выделенных объектов уровня абстракции. Объекты уровня абстракции являются единственными объектами, которым разрешено иметь указатели на объекты, созданные на уровне реализации. При замене библиотек DLL сначала выполните итерацию всех объектов уровня абстракции и попросите их освободить данные, специфичные для реализации. Затем выгрузите DLL и загрузите новую DLL. Затем снова выполните итерацию объектов уровня абстракции и попросите их создать новые данные, специфичные для реализации.

0 голосов
/ 20 января 2009

Хорошо, я думаю, что лучший выбор - использовать COM-подход для опроса библиотеки DLL, чтобы узнать, можно ли ее выгрузить. Как я могу сделать это, хотя, чтобы я мог продолжить опрос DLL после того, как все остальное закрылось (то есть основной поток завершился)? Нужно ли мне создавать полностью отдельный процесс, чтобы сделать это, и в таком случае, как мне это сделать, чтобы этот отдельный процесс знал обо всех загруженных библиотеках и таким образом, который ОЧЕНЬ мало влиял на производительность?

Может, я мог бы просто создать систему опроса, когда все DllPtr выйдут за рамки, и прекратить его, как только он освободит dll? Таким образом, он существует только столько времени, сколько требуется для уничтожения любых оставшихся умных указателей.

0 голосов
/ 20 января 2009

MSDN явно относится к этой теме: «Поток, который должен выгрузить DLL, в которой он выполняется, а затем завершить себя, должен вызывать FreeLibraryAndExitThread вместо вызова FreeLibrary и ExitThread отдельно. В противном случае может возникнуть условие гонки. Подробнее см. В разделе «Примечания» FreeLibraryAndExitThread.

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