2012-01-22 4 views
2

В моей по умолчанию (полный доверия) AppDomain Я хочу, чтобы создать песочницу AppDomain и подписаться на событие в нем:Почему я не могу подписаться на событие в частичном доверенности AppDomain?

class Domain : MarshalByRefObject 
{ 
    public event Action TestEvent; 
} 

Domain domain = AppDomainStarter.Start<Domain>(@"C:\Temp", "Domain", null, true); 
domain.TestEvent +=() => { }; // SecurityException 

Подписка завершается с сообщением «Запрос на разрешение типа«System.Security. Permissions.ReflectionPermission, mscorlib, Version = 4.0.0.0 ... "failed."

(Для определения AppDomainStarter см my answer to another question.)

Обратите внимание, что ApplicationBaseC: \ Temp является НЕ папку, которая содержит сборку, которая содержит домен. Это преднамеренно; моя цель - загрузить вторую стороннюю ненадежную сборку внутри нового AppDomain, и эта вторая сборка находится в C: \ Temp (или где-нибудь еще, может быть, даже сетевой ресурс). Но прежде чем я смогу загрузить вторую сборку, мне нужно загрузить мой класс Domain внутри нового AppDomain. Это удается, но по какой-то причине я не могу подписаться на событие через границу приложения (я могу вызывать методы, но не подписаться на события).

UPDATE: Очевидно, что при подписке на событие в песочнице AppDomain как метод абонента, так и класс, содержащий подписчика, должны быть общедоступными. Например:

public static class Program 
{ 
    class Domain : MarshalByRefObject 
    { 
     public event Action TestEvent; 
     public Domain() { Console.WriteLine("Domain created OK"); } 
    } 
    static void Main() 
    { 
     string loc = @"C:\Temp"; 
     Domain domain = AppDomainStarter.Start<Domain>(loc, "Domain", null, true); 
     // DIFFERENT EXCEPTION THIS TIME! 
     domain.TestEvent += new Action(domain_TestEvent); 
    } 
    public static void domain_TestEvent() { } 
} 

Однако STILL не может подписаться на мероприятие. Новая ошибка: «Не удалось загрузить файл или сборку TestApp, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null» или одну из его зависимостей. Система не может найти указанный файл ».

В некотором смысле это имеет смысл, потому что я указал «неправильную» папку «C: \ Temp» как ApplicationBase моего нового AppDomain, но в некотором смысле это не имеет никакого смысла, потому что сборка «TestApp» равна уже загружен в оба AppDomains. Как возможно, что CLR не может найти сборку, которая уже загружена?

Кроме того, нет никакой разницы, если добавить разрешение на доступ к папке, которая содержит мою сборку:

string folderOfT = Path.GetFullPath(Path.Combine(typeof(T).Assembly.Location, "..")); 
permSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, folderOfT)); 
// Same exception still occurs 

я могу «исправить» эту проблему, используя различные значения для AppDomainSetup.ApplicationBase:

string loc = Path.GetFullPath(Assembly.GetExecutingAssembly().Location + @"\.."); 

Это исключает исключение, но я не могу использовать это «решение», потому что целью AppDomain является загрузка ненадежной сборки из другой папки, кроме папки, содержащей мою собственную сборку. Поэтому loc должен быть папкой, в которой содержится ненадежная сборка, а не та, которая содержит мою сборку.

ответ

0

Исключение составляет от части долевого доверия, а не от полного доверия. Вы, должно быть, отказались предоставить ReflectionPermission в домене частичного доверия

+0

(i) Я не пытаюсь сделать какое-либо отражение, (ii) ReflectionPermission предоставляет доступ для доступа к частным членам любого доступного типа; AFAIK НЕ требуется для доступа к открытым членам. Я не хочу предоставлять ReflectionPermission, потому что он может открыть дыры в безопасности. – Qwertie

+0

(i), как вы видели, это не обязательно ваш код, но инфраструктура, которая должна это сделать (вот почему создание публичного типа решило эту проблему). Ii) достаточно справедливо, и вы все равно обходите эту проблему. –

+0

Можете ли вы не сделать в папку «plug in» находится подпапка существующей базы приложений? то вам не нужно изменять приложение вашего домена приложения, и сборка будет загружена правильно. Вы всегда можете ограничить файл ввода-вывода в каталоге подключений через FileIOPermission –

0

Ассембли разрешаются на основе каждого приложения. Поскольку вы запускаете новый AppDomain в другом каталоге (и ваша сборка не зарегистрирована в GAC), он не может найти сборку.Вы можете изменить код AppDomainStarter для первой загрузки целевой узел, а затем создать экземпляр из этой сборки:

  Assembly assembly = Assembly.LoadFrom(typeof(T).Assembly.ManifestModule.FullyQualifiedName); 
     return (T)assembly.CreateInstance(typeof(T).FullName, false, 0, null, constructorArgs, null, null); 
+0

Код, который вы отправили, будет загружать сборку в том же AppDomain, что и вызывающий, а не в новый AppDomain, и, кроме того, данная сборка ** уже загружена ** (в этом случае T является Program.Domain и Program .Main() уже находится в стеке вызовов!). – Qwertie

0

Единственное, что я мог бы найти, что работает, чтобы поместить узел (который содержит код, который вы хотите запустить в новом AppDomain) в GAC. Конечно, это огромная боль в прикладе, но это единственное, что работает.

Ниже я опишу пару вещей, которые я пробовал, которые НЕ работали.

В некоторых случаях, Visual Studio 2010 даст вам это сообщение при вызове Activator.CreateInstanceFrom (я не уверен, когда именно - чистая консоль приложение не производит это):

Managed Debugging Assistant «LoadFromContext» обнаружил проблему в «C: \ Users ... \ TestApp.vshost.exe». Дополнительная информация: сборка с именем «TestApp» была загружена из 'file: /// C: /Users/.../TestApp.exe', используя контекст LoadFrom. Использование этого контекста может привести к неожиданному поведению для сериализации, кастинг и разрешение зависимостей. Почти во всех случаях рекомендуется избегать контекста LoadFrom. Это можно сделать с помощью установки сборок в глобальном кэше сборок или в каталоге Application Base Base и с использованием Assembly.Load, если явно Загрузка сборок.

Документация Assembly.LoadFrom включает в себя следующее заявление: «Если сборка загружается LoadFrom, а затем сборка в условиях нагрузки пытается загрузить [] же сборку по имени дисплея, попытка загрузки не удалось Это может. возникают, когда сборка де-сериализована ». К сожалению, нет никаких намеков о , почему это случается.

В примере кода, Ассамблея не будучи десериализации (и я не совсем уверен, что это значит для десериализации Ассамблею в первую очередь), но делегат время десериализации; разумно предположить, что десериализация делегата предполагает попытку загрузить ту же сборку «отображаемым именем».

Если бы это было так, то было бы невозможно передать делегат через границы AppDomain, если делегат указывает на функцию, которая находится в сборке, которая была загружена с использованием «контекста LoadFrom». В этом случае, используя CreateInstance вместо CreateInstanceFrom мог избежать этой проблемы (потому что CreateInstanceFrom использует LoadFrom):

return (T)Activator.CreateInstance(newDomain, 
    typeof(T).Assembly.FullName, 
    typeof(T).FullName, false, 
    0, null, constructorArgs, null, null).Unwrap(); 

Но это оказывается красная сельдь; CreateInstance не может быть использован, если ApplicationBase не установлен в папку, содержащую нашу сборку, и если ApplicationBase установлен в эту папку, а затем подписавшись на TestEvent успешно независимо ли CreateInstance или CreateInstanceFrom был использован для создания Т в новом AppDomain. Поэтому тот факт, что T был загружен через LoadFrom, сам по себе не вызывает проблемы.

Еще одна вещь, которую я пробовал, - подписать сборку и рассказать.NET Framework, которая должна быть предоставлена ​​FullTrust:

newDomain = AppDomain.CreateDomain(appDomainName, null, setup, permSet, 
    new StrongName[] { GetStrongName(typeof(T).Assembly) }); 

Это зависит от метода GetStrongName от an MSDN article. К сожалению, это не влияет (т. Е. FileNotFoundException все еще происходит).