Это довольно сложно, но я постараюсь сделать это как можно более ясным. Я создаю расширение визуальной студии, в котором элемент меню добавлен в контекстное меню при щелчке правой кнопкой мыши по проекту (и проект соответствует определенным критериям). Меню должно вызвать процесс, который:Создание доменов и загрузка сборок в 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
загружается больше кода таким образом, который равен а не домен нейтральный, что означает, что домен приложения, который я создаю, должен загрузить его собственную копию. Имеет ли это смысл? Есть ли способ обойти это?