Как сделать провайдер настроек внутри динамически загруженной сборки доступным для размышления?
После этого вопроса я успешно создал своего провайдера пользовательских настроек в устаревшем приложении C#, которое мы разрабатываем. На него ссылаются через SettingsProvider
атрибут:
public sealed class MySettings : SettingsProvider
{
...
}
[SettingsProvider(typeof(MySettings))]
internal sealed partial class Settings {}
Однако теперь я столкнулся с другой проблемой.
Наше клиентское приложение включает в себя средство автообновления, и оно реализовано таким образом, что основная часть клиента, включая вышеупомянутые классы, встроена в DLL (назовем ее здесь client.dll), которая затем используется EXE- файлом. Сначала EXE проверяет наличие обновлений и при необходимости загружает последнюю версию с сервера обновлений, заменяя все библиотеки DLL и т. Д. Их более новой версией (включая client.dll). Чтобы иметь возможность заменять библиотеки DLL во время выполнения, они не могут ссылаться на них статически. Поэтому после обновления он загружает client.dll и запускает его так:
Assembly assy = Assembly.LoadFile(
AppDomain.CurrentDomain.BaseDirectory + "client.dll");
object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);
Печальным последствием этого является то, что фреймворк не может найти мой класс провайдера пользовательских настроек внутри динамически загружаемой сборки. Я пытался использовать LoadFrom
вместо LoadFile
выше, но это не помогло. Единственное работающее решение, которое я нашел до сих пор, - это реализовать прокси-класс в exe-загрузчике с тем же именем, что и у реального провайдера настроек, который полностью найден платформой. Затем прокси-сервер создает экземпляр реального поставщика настроек из клиентской сборки и делегирует ему все вызовы.
Кажется, это работает, но я не доволен этим. Есть ли способ помочь каркасу найти мой класс внутри динамически загружаемой сборки?
Обновить
Сообщение об ошибке я получаю:
System.Configuration.ConfigurationErrorsException: Failed to load provider type: Client.Properties.MySettings, Client, Version=4.0.1341.0, Culture=neutral, PublicKeyToken=null.
at System.Configuration.ApplicationSettingsBase.get_Initializer()
at System.Configuration.ApplicationSettingsBase.CreateSetting(PropertyInfo propInfo)
at System.Configuration.ApplicationSettingsBase.EnsureInitialized()
at System.Configuration.ApplicationSettingsBase.get_Properties()
at System.Configuration.SettingsBase.GetPropertyValueByName(String propertyName)
at System.Configuration.SettingsBase.get_Item(String propertyName)
at System.Configuration.ApplicationSettingsBase.GetPropertyValue(String propertyName)
at System.Configuration.ApplicationSettingsBase.get_Item(String propertyName)
at Client.Properties.Settings.get_SomeConfigSetting()
at ...
Посредством сообщений отладки и регистрации я определил, что метод инициализатора класса никогда не вызывается, поэтому класс никогда не создается. Другими словами, похоже, что за вышеприведенным исключением нет скрытой ошибки инициализации.
Еще один потенциально важный момент: клиент в настоящее время работает на.NET 2.0, и в обозримом будущем не планируется его обновление.
Обновление 2
Я начал исследовать AppDomain, как подсказал ответ @jwddixon. Сначала я хотел проверить, действительно ли Client.dll оказывается в другом домене приложения, чем домен вызывающего EXE-файла. Таким образом, я перечислил сборки в текущем домене приложения и увидел, что Клиент на самом деле там. Но, к моему удивлению, я заметил, что на самом деле в списке есть две сборки с именем Client - и EXE, и DLL имеют одно и то же имя сборки, но разную версию (4.0.0.0 для EXE, 4.0.1352.0 для DLL в настоящее время).). До сих пор я не был полностью осведомлен об этом, и это может быть важно. Я постараюсь изменить имя сборки EXE дальше...
Обновление 3
... и это на самом деле решило проблему! Ааааа... По отношению к моим неизвестным предшественникам, которые изобрели эту искаженную схему, для тех, кто знает почему, у меня очень странные мысли в данный момент... но спасибо всем вам, ребята, за то, что они внесли вопросы и идеи, которые в конечном итоге приводят к решению!
1 ответ
Я не пробовал это, но как насчет чего-то вроде:
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
AppDomain domain = AppDomain.CreateDomain("myAppDomain", null, setup)
setup.ApplicationBase = file;
Assembly assy = domain.Load(AssemblyName.GetAssemblyName(
AppDomain.CurrentDomain.BaseDirectory +
"client.dll"));
object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);