С++ DLL не выгружается с AppDomain

У меня есть плагин С#, который использует отдельную DLL С++. Единственная ссылка на эту DLL - из самого модуля. Родительское приложение загружает все плагины в свой собственный AppDomain и выгружает этот AppDomain, когда плагин выгружается.

Я проверил, и я определенно вижу падение памяти приложения, когда я выгружаю плагин. Я также могу удалить все загруженные управляемые сборки. Проблема в том, что когда я пытаюсь удалить родную DLL, я просто продолжаю получать Access Denied, пока не закрою все приложение.

Я смотрел на это некоторое время, но я все еще не могу понять, почему именно эта DLL остается в памяти.

Ответ 1

AppDomains - это чистая управляемая конструкция кода. В собственном коде ничего подобного не существует, и Windows не знает об этом. Таким образом, область для загруженной собственной DLL - это процесс. Технически, маркерщик pinvoke мог ссылаться на подсчет DLL и отслеживать, какой именно AppDomain запускал загрузку DLL. Однако он не может определить, работает ли какой-либо собственный код, который использует эту DLL. Родительский код, который может быть запущен вызовом, сделанным из кода в другом AppDomain, возможно, косвенно через маршалированный делегат.

Очевидно, что катастрофа удаляется, если менеджер AppDomain выгружает DLL, которая использовалась таким образом, что неприятно и невозможно диагностировать AccessViolation. Особенно неприятно, поскольку он может запускать долгое время после того, как AppDomain получил разгрузку.

Таким образом, маршаллер не выполняет такой подсчет, DLL остается загруженным. Только вы можете предоставить гарантию, что этого не может быть, у вас есть определенный контроль над тем, какой код работает в DLL и как он запускается. Вы можете заставить DLL разгружаться, но это требует взлома. Pinvoke LoadLibrary(), чтобы получить доступ к DLL. И pinvoke FreeLibrary() дважды, чтобы принудительно его выгрузить. Ни Windows, ни CLR не могут видеть, что вы обманываете. Вы должны убедиться, что после этого нельзя использовать DLL.

Ответ 2

AFAIK (под капотом) необходимо загружать собственные DLL файлы через API-интерфейс Win32 LoadLibrary... который загружает их непосредственно в процесс memory - в случае .NET-приложения, которое не является специфичным для AppDomain... LoadLibrary ничего не знает о AppDomain (которое чисто специфично для .NET)... при этом выгрузка AppDomain doesn Обязательно выгружать собственные DLL файлы...

Интересные дискуссии по этой ситуации:

ЕСЛИ вы можете изменить реализацию соответствующего плагина, тогда вы бы реализовали "позднюю нативную привязку", которая решила бы проблему, которую вы видите: