Почему я не могу подписаться на событие в домене с частичным доверием? - PullRequest
2 голосов
/ 22 января 2012

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

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 ... ».»

(определение AppDomainStarter см. мой ответ на другой вопрос .)

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

ОБНОВЛЕНИЕ : Очевидно, что при подписке на событие в песочнице 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() { }
}

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

В некотором смысле, это имеет смысл, потому что я указал «неправильную» папку «C: \ Temp» в качестве ApplicationBase моего нового AppDomain, но в некотором смысле это не имеет никакого смысла, потому что сборка «TestApp» уже загружен в оба домена приложений . Как это возможно, что 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 должна быть папкой, содержащей ненадежную сборку, а не той, в которой содержится моя сборка.

Ответы [ 3 ]

0 голосов
/ 06 марта 2012

Сборки разрешаются для каждого домена приложений.Поскольку вы запускаете новый домен приложений в другом каталоге (а ваша сборка не зарегистрирована в 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 голосов
/ 08 марта 2012

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

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

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

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

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

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

Если бы это было правдой, было бы невозможно передать делегат через границы 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 IS установлен на эту папку, тогда подписка на TestEvent завершается успешно независимо от того, от CreateInstance илиCreateInstanceFrom был использован для создания T в новом домене приложений.Следовательно, тот факт, что T был загружен через LoadFrom, сам по себе не вызывает проблемы.

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

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

Это зависит от метода GetStrongName из статьи MSDN .К сожалению, это не имеет никакого эффекта (т.е. исключение FileNotFoundException по-прежнему возникает).

0 голосов
/ 23 января 2012

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

...