У меня довольно специфическая проблема с моим приложением Silvelight 5:
У меня есть приложение, которое динамически загружает библиотеки Silverlight (может быть много), и эти библиотеки вызывают методы через dllimport.
Причина: приложение для разных сканирующих устройств. Библиотека Silverlight для логики, библиотека C ++ от производителя и подключается к устройству. Может быть много библиотек silverlight, и они должны быть загружены во время выполнения.
Проблема: после загрузки библиотеки Silverlight и сканирования — я не могу получить доступ к файлу с библиотекой C ++. Почему я должен? Если пользователь пытается выбрать сканер, я вычисляю хеш-код библиотеки, чтобы проверить, есть ли на сервере более новая версия.
Моя реализация:
ViewModel вызывает метод для получения текущего класса сканера:
using (var stream = new IsolatedStorageFileStream(scanner.DirectoryName + Path.DirectorySeparatorChar + scanner.ScannerLibraryName, System.IO.FileMode.Open, store))
{
AssemblyPart assemblyPart = new AssemblyPart();
Assembly assembly = assemblyPart.Load(stream);
return (Scanner)assembly.CreateInstance(scanner.ClassName);
}
Позже есть сканирование:
this.userScanner.LoadProducerLibrary();
result = userScanner.ScanDocument();
this.userScanner.FreeProducerLibrary();
LoadProducerБиблиотека звонков:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr LoadLibrary(string lpFileName);
ScanDocument вызывает некоторые методы третьей части также через dllimport.
FreeProducerLibray вызывает:
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] /* unnecessary, isn't it? */
public static extern bool FreeLibrary(IntPtr hModule);
После этого у меня есть метод в другой viewmodel, который пытается открыть файл библиотеки производителя для вычисления хеш-кода, и я получаю «Не удается получить доступ к файлу, потому что он используется другим процессом».
Я пытался вычислить хэш-код в классе Scanner (динамически загружать сборку), и все было в порядке, поэтому я подозреваю, что это именно этот процесс. Но почему FreeLibrary не достаточно?
Не могли бы вы указать мне несколько советов? Стоит ли как-то распоряжаться полем userScanner?
ScanDocument вызывает некоторые методы третьей части также через dllimport.
Вы исключили самый важный код из вопроса, но этого утверждения достаточно для диагностики вашей проблемы. Маршаллер pinvoke также вызывает LoadLibrary () для DLL. Таким образом, счетчик ссылок на DLL два. Ваш FreeLibrary () вызов не выгрузите его, и файл останется заблокированным.
Вы можете обойти это, вызвав FreeLibrary () еще раз. Тот будут выгрузите DLL, но это также вооружает бомбу замедленного действия в вашей программе. Вы не можете безопасно сделать эти звонки снова. Маршаллер pinvoke по-прежнему убежден, что DLL загружена, поэтому он не будет повторно генерировать заглушки, которые он генерировал для импортированных функций. Они указывают на адреса, по которым функции загружались раньше, а не там, где они загружены сейчас. Будет казаться работать, особенно когда вы делаете это в искусственном тесте. Но рано или поздно DLL не может быть перезагружена по тому же адресу снова, потому что адресное пространство для нее использовалось для чего-то другого, и ваша программа потерпит неудачу из-за серьезного сбоя.
Если вы хотите сделать эту работу, то вы не можете использовать [DllImport] для этих методов. Вместо этого вы должны выполнить ту работу, которую выполняет маршаллер pinvoke, и вызвать GetProcAddress () для каждой используемой вами точки входа и сделать ее вызываемой с помощью Marshal.GetDelegateForFunctionPointer (), используя объявленные вами делегаты, которые соответствуют сигнатуре встроенной функции. Работает, это не красиво.
Лучшей альтернативой является использование отдельного вспомогательного процесса, который выполняет эти вызовы и взаимодействует с ним. Теперь это безопасно, разрешение того, что вспомогательный процесс завершается, гарантирует, что заглушки pinvoke исчезнут, а DLL разблокирована. Не уверен, что практично в Silverlight, он не был действительно разработан с взаимодействием процессов высоко в списке функций. Нет класса Process, который вносит определенные ограничения в подход.