Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Загрузка сборки в домен из произвольного каталога / 22 сообщений из 22, страница 1 из 1
18.10.2011, 10:31
    #37486250
2k
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
18.10.2011, 10:46
    #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
18.10.2011, 11:38
    #37486396
pation
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Загрузка сборки в домен из произвольного каталога
2k,

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

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

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

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

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

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

tAZARА с загрузкой сборок PrivateBinPath, надеюсь, помог. Есть еще 1 нюанс - при загрузке сборки в домен она грузится и в основной домен приложения тоже (по умолчанию). Спасает DoCallBack у домена и вызов Assembly.Load.
Честно говоря у меня так и не получилось это. Указываю ApplicationBase, в документации MSDN было написано, что PrivateBinPath относительно его указывается. Пробовал и так и так. =`(
...
Рейтинг: 0 / 0
18.10.2011, 12:59
    #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
18.10.2011, 14:32
    #37486850
2k
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
18.10.2011, 14:58
    #37486904
tAZAR
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Загрузка сборки в домен из произвольного каталога
2kЧто я делаю не так?
Не указываете полный путь к каталогам в PrivateBinPath, не указываете путь к базовому каталогу основного домена (если нужен).

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

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

Если указан PrivateBinPath, PrivateBinPathProbe должен быть "".
Нужен фабричный метод или прокси для создания типов из загруженной сборки в нужном домене. Помогает написание своей обертки над доменом, загрузка, повторюсь, через DoCallback (суть в том, чтобы выполнить код и загрузку сборки именно в том домене, который вам нужен).
...
Рейтинг: 0 / 0
19.10.2011, 11:51
    #37488478
2k
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
19.10.2011, 12:05
    #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
19.10.2011, 12:13
    #37488527
tAZAR
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Загрузка сборки в домен из произвольного каталога
А вот пример работы с доменами и фабрикой, работающей в создаваемых доменах, тоже давно здесь выкладывал. Похожая тема была, кстати :)
Там в папке с тестом должен быть файл "file.txt". Иначе упадет, кажется.
...
Рейтинг: 0 / 0
19.10.2011, 12:51
    #37488630
2k
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
19.10.2011, 13:09
    #37488685
tAZAR
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Загрузка сборки в домен из произвольного каталога
2k,

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

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

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

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


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