2016-08-02 3 views
0

Это довольно сложно, но я постараюсь сделать это как можно более ясным. Я создаю расширение визуальной студии, в котором элемент меню добавлен в контекстное меню при щелчке правой кнопкой мыши по проекту (и проект соответствует определенным критериям). Меню должно вызвать процесс, который:Создание доменов и загрузка сборок в Visual Studio extension

1) Построить проект

2) Создать новую AppDomain

3) Загрузить новую сборку в новую AppDomain

4) Возвратите некоторый объект из нового AppDomain, который определен в третьей сборке (т. Е. Не сборке расширения или построенном проекте), но ссылается на как на два других.

Проблема, с которой я постоянно сталкиваюсь, связана с номером 4 и убеждает расширение, что тип из третьего узла (назовем его foo.dll и тип FooAssembly) - это тот же тип.

В моем расширении (давайте называть это extension.dll) у меня есть это:

// assemblyPath is the path to the dll I just build for the selected project 
var setup = new AppDomainSetup() 
{ 
    ApplicationBase = System.IO.Path.GetDirectoryName(assemblyPath) 
}; 

domain = AppDomain.CreateDomain("Test_AppDomain", AppDomain.CurrentDomain.Evidence, setup); 

// FooAssembly is defined in foo.dll and is reference by *both* the project.dll and 
// but the extension.dll it exists both in the assemblyPath folder 
// and typeof(FooAssembly).Assembly.Location which is NOT the same as 
// the current AppDomain's CodeBase because the current CodeBase is the 
// CodeBase of Visual Studio in this context 
var obj = domain.CreateInstanceAndUnwrap(
    typeof(FooAssembly).Assembly.FullName, 
    typeof(FooAssembly).FullName, 
    false, 
    BindingFlags.Default, 
    null, 
    new object[] { assemblyPath, typeof(FooAssembly).Assembly.Location }, null, null); 

// This says the type is MarshalByRefObject 
System.Diagnostics.Debug.WriteLine(obj.GetType().Name); 

// this sets collection to null because it can't cast it 
// which is essentially my problem 
collection = obj as FooAssembly; 

Теперь мой FooAssembly, определенный в foo.dll и ссылаются как extension.dll и project.dll выглядит следующим образом:

public class FooAssembly: MarshalByRefObject 
{ 
    private Assembly _assembly; 

    public FooAssembly(string assemblyPath, string parentPath) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve += (o, e) => 
     { 
      // this event never seems to get fired 
      System.Diagnostics.Debug.WriteLine($"attempt to resolve {e.Name} for {e.RequestingAssembly.FullName}"); 
      return null; 
     }; 
     // this didn't seem to help 
     //Assembly.LoadFrom(parentPath);  // load spider assembly from same place (hopefully)... 
     // I've tried LoadFrom as well, with the same result 
     _assembly = Assembly.LoadFile(assemblyPath); 
    } 

    public override object InitializeLifetimeService() 
    { 
     return null; 
    } 
} 

Так что на самом деле происходит здесь и как я могу заставить себя вести себя? Мне нужен AppDomain, который я создал для загрузки собранной мной сборки, а затем загружаю его зависимости (или, по крайней мере, foo.dll) из того же места, где расширение получает его, я думаю. Или, по крайней мере, мне нужно знать, что мой прозрачный прокси -FooAssembly.

EDIT: Я попытался скопировать мое project.dll в ту же папку, extension.dll так, что она будет иметь для загрузки с того же места. Это не имеет никакого значения, так что я пытался вставлять эти строки в моем FooAssembly конструктор:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName); 
System.Diagnostics.Debug.WriteLine(this.GetType().FullName); 
System.Diagnostics.Debug.WriteLine(this.GetType().Assembly.CodeBase); 

И я могу видеть, что а) я в созданном домене. б) У меня есть подходящий класс, и в) Я, кажется, имею правильный путь?!? CodeBase возвращается как:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/Foo.DLL

Так в моем домене по умолчанию после создания домена и, прежде чем пытаться создать объект, я делаю это:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName); 
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).FullName); 
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).Assembly.CodeBase); 

А вот мой код база:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/foo.dll

Единственное отличие здесь в том, корпус на foo.dll vs Foo.DLL, который, я считаю, не должен быть значительным.

Я попытался это (вдохновленные this):

dynamic dobj = obj; 
string l2 = dobj.GetType().Assembly.CodeBase; 
System.Diagnostics.Debug.WriteLine(l2); 

И это явно думает, что это MarshalByRefObject загружается из file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll, так что кажется, что это не правильно разворачивать?

Редактировать: После прочтения об этом вокруг, у меня есть теория. Для foo.dll, который будет использоваться между доменами, он должен быть загружен «без домена», и моя нынешняя теория заключается в том, что мое расширение, вероятно, изолировано в собственном домене визуальной студией, и поэтому foo.dll загружается больше кода таким образом, который равен а не домен нейтральный, что означает, что домен приложения, который я создаю, должен загрузить его собственную копию. Имеет ли это смысл? Есть ли способ обойти это?

ответ

1

После многократного поиска и повторной попытки изменить код, я в конце концов сломал это, когда нашел ProvideBindingPathAttribute.

Я добавил [ProvideBindingPath] к моему классу упаковки, и проблемы, которые меня преследовали, ушли. Мне больше не нужно было обходиться с ApplicationBase для моего домена и мог просто пройти AppDomain.CurrentDomain.SetupInformation для AppDomainSetup для моего нового AppDomain.

Смежные вопросы