powered by simpleCommunicator - 2.0.56     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Загрузка сборки в домен из произвольного каталога
22 сообщений из 22, страница 1 из 1
Загрузка сборки в домен из произвольного каталога
    #37486250
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здравствуйте, хотел обратиться к Вам со следующей проблемой.
Генерирую две сборки с помощью встроенного C# компилятора кода. Первая запускаемая, а вторая с так называемыми вспомогательными пользовательскими функциями.
Проблема именно в том, чтобы загрузить их одновременно в домен, для возможности дальнейшей выгрузки, т.к. загруженную сборку можно выгрузить лишь выгрузив её домен. Загрузка сборок должна производиться из определенного каталога, который не связан с приложением (Допустим C:\Libraries). В этом главная проблема, потому что если положить сборки в каталог запускаемого приложения, то все загружается легко.

Запускаемая библиотека:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public partial class Runner
{
	public static string Run()
	{
		return "SomeMethod:" + ClassLib.SomeMethod();
	}

	public string GetVersion()
	{
		return "v01";
	}
}

Код: plaintext
1.
2.
3.
4.
5.
6.
public class ClassLib //: MarshalByRefObject
{
	public static string SomeMethod()
	{
		return "Invoke SomeMethod";
	}
}

Перепробовал не мало способов, и мои пояснения. Поправьте, если я не прав.

Загрузка в текущий домен, что изначально не подходит, но все же привел его. Как я понял данным метод загружается вне контекста, где дополнительно невозможно загружать другие сборки
Код: plaintext
Assembly assembly = Assembly.LoadFile(path_run);

Подписываюсь на событие при невозможности загрузки из каталога приложения. Обманный ход, но не работает.
Код: plaintext
1.
2.
3.
AppDomain ad = AppDomain.CreateDomain("sd");
ad.AssemblyResolve += delegate(object sender, ResolveEventArgs args) { return Assembly.LoadFile(args.Name); };
Assembly assembly = ad.Load(path_run);
Invoke(assembly);

Метод Invoke я произвожу поиск класса и его метода по сборке и потом запускаю. Что то вроде этого
Код: plaintext
var result = assembly.GetType("TestInvoke.Run.Runner").GetMethod("Run").Invoke(null, null);

Метод CreateInstanceFromAndUnwrap. Как я понял, то необходимо объявлять через интерфейс, наследоваться от MarshalByRefObject и применение флага [Serializable]. Это работает, но жутко не удобно, потому что в реальном приложении объявления этого флага [Serializable] необходимо у 90% всех классов! o_O
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
AppDomain ad = AppDomain.CreateDomain("sd1");
try
{
	IRunner runner = (IRunner)ad.CreateInstanceFromAndUnwrap(
		Path.GetDirectoryName(path_run) + "\\TplRunerI.dll", 
		"TestInvoke.Run.Runner");
	object result = runner.GetVersion();
	Console.WriteLine(result as string);
}
finally
{
	AppDomain.Unload(ad);
}

Пробовал и с применением класса AppDomainSetup, в котором можно указать ApplicationBase - каталог с приложением, а именно там сначала проводится поиск необходимых сборок. Но как будто он не реагирует на это поле.

Вот мой тестовый проект.
http://depositfiles.com/files/25mk9qtff
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486283
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,

Для поиска сборок:
Код: plaintext
1.
2.
3.
4.
5.
AppDomainSetup m_Setup;
///
m_Setup = new AppDomainSetup();
m_Setup.PrivateBinPathProbe = "";
m_Setup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;

Для проброски ссылок через домен можно использовать интерфейсы и MarshalByRefObject.
Для указания того, в какие домены будет загружена сборка существует 2 способа:
Настройка m_Setup.LoaderOptimization при создании домена, а также установка атрибута LoaderOptimization методу Main в основном домене. Подробнее про LoaderOptimization очень хорошо написано в MSDN.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486396
Фотография pation
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,

1. читаем файл в массив байт
2. грузим массив в домен, c помощью Load
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486458
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZAR...
Чтобы использовать MarshalByRefObject, как я понимаю, необходимо чтобы все входные параметры были Serializable. Что описано в одном из моем подходе.

[quot pation]...
2kКак я понял данным метод загружается вне контекста, где дополнительно невозможно загружать другие сборки

То есть, загрузка просто загрузчика пройдет, но если в будет необходима ссылка на вторую библиотеку, то её попросту не найдет.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486522
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,

Если я правильно вас понял:
При использовании MarshalByRefObject ссылка на него пробрасывается как TransparentProxy. Serializable не нужен. Если вы о параметрах, передаваемых в его методы - то потребуется либо наследовать их от MarshalByRefObject, либо метить как Serializable, да.
А с загрузкой сборок PrivateBinPath, надеюсь, помог. Есть еще 1 нюанс - при загрузке сборки в домен она грузится и в основной домен приложения тоже (по умолчанию). Спасает DoCallBack у домена и вызов Assembly.Load.
Сам давно посматриваю в сторону MEF, где есть гора фенечек, время бы найти..
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486556
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZAR2k,

Если я правильно вас понял:
При использовании MarshalByRefObject ссылка на него пробрасывается как TransparentProxy. Serializable не нужен. Если вы о параметрах, передаваемых в его методы - то потребуется либо наследовать их от MarshalByRefObject, либо метить как Serializable, да.

Да правильно. Но тут вытекает следующее - передаваемый параметр не примитив, следовательно все ссылки на не примитивные классы в нем тоже необходимо пометить Serializable или наследовать от MarshalByRefObject. Это своего рода костыль, простите.

tAZARА с загрузкой сборок PrivateBinPath, надеюсь, помог. Есть еще 1 нюанс - при загрузке сборки в домен она грузится и в основной домен приложения тоже (по умолчанию). Спасает DoCallBack у домена и вызов Assembly.Load.
Честно говоря у меня так и не получилось это. Указываю ApplicationBase, в документации MSDN было написано, что PrivateBinPath относительно его указывается. Пробовал и так и так. =`(
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486623
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,

Вот пример поподробнее:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
 AppDomainSetup m_Setup;

 public static string PrivateBinPath {
            get {
                return AppDomain.CurrentDomain.BaseDirectory + ";" +
                    AppDomain.CurrentDomain.BaseDirectory + "Components;" +
                    AppDomain.CurrentDomain.BaseDirectory + "Basic;" +
                    AppDomain.CurrentDomain.BaseDirectory + "Runtime;" +
                    AppDomain.CurrentDomain.BaseDirectory + "Dll;" +
                    ...
                ;
            }
        }


 AppDomain CreateApplicationDomain() {
            m_Setup = new AppDomainSetup();
            m_Setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            m_Setup.PrivateBinPathProbe = "";
            m_Setup.PrivateBinPath = PrivateBinPath;
            ...
 }

Дело в том, что домены обмениваются информацией при помощи механизмов Remoting (через прокси и сериализация), поэтому вряд ли получится найти что-то новое. Есть еще AppDomain.SetData, но там те же грабли.
Кстати, проблему перезагрузки сборок также можно решить, включив у домена теневое копирование, вкрутив IoC и используя загрузку сборок через массивы. В принципе, даже без доменов. Выгрузки не будет, больная это тема в дотнете...
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486850
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZAR,

Вообщем делаю так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
            AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
            ads.ApplicationBase = Path.GetDirectoryName(dir_dll);
            ads.ApplicationName = Path.GetFileName(dir_dll);
            ads.PrivateBinPath = "dll"; 
            ads.PrivateBinPathProbe = "";
            ads.LoaderOptimization = LoaderOptimization.SingleDomain;

            AppDomain ad = AppDomain.CreateDomain("zxc", null, ads);
            try
            {
                Console.WriteLine("Application base is: " + ad.SetupInformation.ApplicationBase);
                Assembly assembly1 = ad.Load(AssemblyName.GetAssemblyName(path_lib));
...

Пробовал и сразу ApplicationBase указывать на dir_dll, тогда PrivateBinPath = "". Эффекта не было.
Что я делаю не так?
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37486904
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2kЧто я делаю не так?
Не указываете полный путь к каталогам в PrivateBinPath, не указываете путь к базовому каталогу основного домена (если нужен).

С AppDomain.Load есть проблемы - как не бейся, грузит в оба домена. Спасает DoCallback.

Код: plaintext
1.
Assembly assembly1 = ad.Load
Когда пишите так - грузите сборку в оба домена (прокидывается ссылка, поднимаются все связанные референсы, типы).

Если указан PrivateBinPath, PrivateBinPathProbe должен быть "".
Нужен фабричный метод или прокси для создания типов из загруженной сборки в нужном домене. Помогает написание своей обертки над доменом, загрузка, повторюсь, через DoCallback (суть в том, чтобы выполнить код и загрузку сборки именно в том домене, который вам нужен).
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37488478
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MSDNЕсли заданные для PrivateBinPath каталоги не находятся в папке ApplicationBase, они игнорируются.

Я попытался сделать обертку, вот что получилось:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
public class MyDomain
    {
        public AppDomain domain;
        public Dictionary<string, Assembly> assembles;
        Assembly assembly;

        public MyDomain(string pathDll)
        {
            //domain = AppDomain.CreateDomain("ARM Domain", null, pathDll, pathDll, true); // с теневым копированием
            domain = AppDomain.CreateDomain("ARM Domain", null, new AppDomainSetup() { ApplicationBase = pathDll }); // через настройки
            assembles = new Dictionary<string, Assembly>();
        }

        public Assembly LoadFrom(string path)
        {
            domain.DoCallBack(() => assembly = Assembly.LoadFrom(path));//LoadFile(path));//Load(System.IO.File.ReadAllBytes(path));
            assembles.Add(Path.GetFileName(path), assembly);
            
            return assembly;
        }
    }

Но ловлю ошибку, которую не могу осмыслить:
Тип "TestInvoke.ARMDomain+<>c__DisplayClass1" сборки "TestInvoke, Version=1.0.0.2, Culture=neutral, PublicKeyToken=null" не помечен как сериализуемый.

Хотя без обертки библиотека загружается, но возвратить Assembly не могу.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37488508
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,
А сборку и ненужно возвращать, иначе пропадает смысл домена. (Сборка вместе с ее типами загрузится и туда, куда вы ее вернете).
Вот вам пример, давно его здесь выкладывал.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
using System;
using System.Reflection;

namespace xxxxxxxxxxx.Common {
    public delegate void DomainAssemblyLoaded(Assembly assembly);

    public class DomainAssemblyLoader:IDisposable {
        private string m_AssemblyFile;
        private AppDomain m_Domain;

        public AppDomain Domain { get { return m_Domain; } }

        public  DomainAssemblyLoaded OnDomainAssemblyLoaded;

        public DomainAssemblyLoader(string assemblyFile) {
            m_AssemblyFile = assemblyFile;
            AppDomainSetup m_Setup = new AppDomainSetup();
            m_Setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            m_Setup.PrivateBinPathProbe = "";
            m_Setup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
            m_Setup.LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization;
            m_Setup.ShadowCopyFiles = AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles;
            m_Setup.ShadowCopyDirectories = AppDomain.CurrentDomain.SetupInformation.ShadowCopyDirectories;
            m_Setup.CachePath = AppDomain.CurrentDomain.SetupInformation.CachePath;
            m_Setup.ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName+"loader_inst";
            System.Security.PermissionSet set = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
            m_Domain = AppDomain.CreateDomain(m_Setup.ApplicationName, AppDomain.CurrentDomain.Evidence, m_Setup, set, null);
        }

        public void SetDomainData(string name,object data) {
            m_Domain.SetData(name, data);
        }

        public object GetDomainData(string name) {
            return m_Domain.GetData(name);
        }

        public void Dispose() {
            UnloadAssembly();
        }

        public void LoadAssembly() {
            m_Domain.DoCallBack(DomainLoadAssembly);
        }

        public void UnloadAssembly() {
            if(m_Domain != null)
                AppDomain.Unload(m_Domain);
        }

        private void DomainLoadAssembly() {
            Assembly asm = Assembly.LoadFrom(m_AssemblyFile);
            if (OnDomainAssemblyLoaded != null)
                OnDomainAssemblyLoaded(asm);
        }
    }
    
}

Суть в чем - событие OnDomainAssemblyLoaded будет выполняться в том же домене, а не в вызывающем.
Нужно внимательно следить за контекстами работы кода (легко, например, случайно вернуть тип в основной домен, вызвав метод GetType у ссылки на прокси или посмотрев значения переменных в отладчике).
Если вам требуется создавать экземпляры объектов плагина в основном домене - сделайте обертке специальный метод, который будет создавать экземпляр и верните известный обоим доменам интерфейс. Иначе объект распакуется и тип будет загружен.
Единственный способ выполнить код в определенном домене - вызов через DoCallback.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37488527
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А вот пример работы с доменами и фабрикой, работающей в создаваемых доменах, тоже давно здесь выкладывал. Похожая тема была, кстати :)
Там в папке с тестом должен быть файл "file.txt". Иначе упадет, кажется.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37488630
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZARА вот пример работы с доменами и фабрикой, работающей в создаваемых доменах, тоже давно здесь выкладывал. Похожая тема была, кстати :)
Там в папке с тестом должен быть файл "file.txt". Иначе упадет, кажется.
Кстати на днях разбирал этот пример. Там все замечательно работает вот по этой строчке:

Код: plaintext
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
Ну или в других случая, когда ниже по дереву
Код: plaintext
File.Copy(AppDomain.CurrentDomain.BaseDirectory+"\\temp\\"+dynDllName,AppDomain.CurrentDomain.BaseDirectory+"\\"+dynDllName,true);

С вашим примером сейчас разбираюсь.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37488685
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,

В прикрепленном в архиве примере у меня суть в том, что фабрика создается внутри отдельного домена (в него же и загружаются сборки). Создает экземпляры классов она тоже внутри контекста выполнения отдельного домена, а ссылки уже возвращает наружу.
AppDomain.CurrentDomain в фабрике используется, т.к. есть уверенность, что текущий домен для фабрики - тот самый новый домен.
По консольному выводу это будет видно, также как и будет видно отсутствие множественной загрузки сборок во все домены приложения. Пример с CodeDom показыват, что сборка создается также в нужном домене.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37489022
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tAZAR,
При загрузке CreateInstanceFromAndUnwrap, то в качестве входных параметров для реализуемого в плагине сборке методу необходимо передавать только сериализуемые или наследуемые от MarshalByRefObject объекты.
Один из объектов содержит экземпляр системного класса, который не сериализуемый (System.IO.BinaryReader). Тупик. =)
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37489063
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2k,
Оборачивайте BinaryReader и все, что не унаследовано от MarshalByRefObject, либо не сериализуется в свои заместители/прокси объекты, унаследованные от MarshalByRefObject.
Здесь нужно определиться - будете ли вы передавать копии объектов, либо вам нужны разделяемые между доменами экземпляры.

Я уже писал, что домены работают через Remoting. А там либо прокси (маршаллинг), либо сериализация.
Еще 1 путь взаимодействия между доменами - контракты, WCF. С пушки по воробьям.

Микрософт в 2004 году писал в одном из ответов на тему выгрузки сборок, что они планируют ее реализовать в следующих версиях. Вот уже 2011й наступил, а у нас все те же домены, костыли и сожранная под них память...
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37489082
Фотография tAZAR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, пробросить таким способом поток - цель смутно мне понятная (тем более, через сериализацию) :) Достаточно прокси к потоку в основном домене и возможности работать с этим экземпляром во втором домене - 100%. Либо передавайте содержимое потока как массив, если достаточно копии (+съеденная память).
При работе с доменами приходится немного по другому думать, и способы с простой передачей объекта, содержащего "все что нужно", не прокатывают.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37490503
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Действительно было не логично передавать объект, содержащего "все что нужно". Сделал реорганизацию по разделению модели и её заполнение. Но вот одно я перенести не смог. Я в качестве параметра передаю ссылку на метод, так называемый "call back". Как мне это абстрагировать, ума не приложу. :)
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37490663
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вообщем решил просто отказаться от call back.
Так что проблема решена. Огромное спасибо tAZAR.
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #37492482
2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Все же одна проблема возникла. Загрузка через AppDomain.CreateInstanceFromAndUnwrap занимает огромное количество времени (я так понимаю это в связи с сериализацией объектов), в отличии от Assembly.LoadFrom. Все бы хорошо, если бы при LoadFrom не блокировался файл.
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
Загрузка сборки в домен из произвольного каталога
    #38512390
Stanislav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2kВообщем решил просто отказаться от call back.
Так что проблема решена. Огромное спасибо tAZAR.

Скажи пожалуйста, как решил проблему? а то че-то никак не въеду сто делать с этими доменами... (
...
Рейтинг: 0 / 0
Загрузка сборки в домен из произвольного каталога
    #38512559
beg-in-er
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
StanislavСкажи пожалуйста, как решил проблему? а то че-то никак не въеду сто делать с этими доменами... (
а в чём трабла?
pation описал то, как надо правильно сборку подгружать.
...
Рейтинг: 0 / 0
22 сообщений из 22, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Загрузка сборки в домен из произвольного каталога
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]