如何跨 AppDomain 边界传递强类型的 MarshalByRefObject?
How to pass a strongly typed MarshalByRefObject across an AppDomain boundary?
我有一个场景,我想实现父 AppDomain
和它的子之间的简单通信。我正在尝试做的事情看起来会奏效,但我遇到了一个障碍,下面的代码片段就是一个例子。
程序导致消息未找到 C::.ctor(System.MarshalByRefObject) 的构造函数,因此看起来运行时已拒绝接受参数的构造函数我传递给 CreateInstanceFromAndUnwrap
调用的类型。我可以理解为什么实例化 B
有效并且我希望 C
表现相同,那为什么不呢?
谢谢!
编辑:在 Hans 指出我的第一个示例中的一个普通错误之后,我对下面的示例代码进行了小幅更新。因此,虽然我们的子应用程序域上仍然有一个虚构的 ApplicationBase
属性,但我们可以看到加载了正确的程序集,并且可以看到我们尝试调用的构造函数。
Hans 的观察 确实 使代码工作,但我不认为我可以将真实事物中的 ApplicationBase 设置为父域的基础。
循环第二次迭代的输出现在如下:
C
初始加载:mscorlib
展开后:mscorlib
展开后:Foo
虚空.ctor(A)
找不到类型 'C' 的构造函数。
using System;
using System.Linq;
using System.Reflection;
class A : MarshalByRefObject { }
class B : MarshalByRefObject
{
public B(MarshalByRefObject obj) { }
}
class C : MarshalByRefObject
{
public C(A obj) { }
}
static class Program
{
static void Main(string [] args)
{
foreach (var type in new [] { typeof(B), typeof(C) })
{
try
{
Console.WriteLine(type.FullName);
var setup = CreateSetup();
var domain = AppDomain.CreateDomain("foo", AppDomain.CurrentDomain.Evidence, setup);
foreach (var ass in domain.GetAssemblies())
{
Console.WriteLine("Initially Loaded: {0}", ass.GetName().Name);
}
domain.CreateInstanceFromAndUnwrap(typeof(A).Assembly.Location, typeof(A).FullName);
foreach (var ass in domain.GetAssemblies())
{
Console.WriteLine("After Unwrap: {0}", ass.GetName().Name);
}
var constructors = domain.GetAssemblies()
.ToList()
.SelectMany(ass => ass.DefinedTypes)
.First(t => t.Name == "C")
.GetConstructors();
foreach (var constructor in constructors)
{
Console.WriteLine(constructor);
}
CreateWrapped(domain, type);
AppDomain.Unload(domain);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
static AppDomainSetup CreateSetup()
{
return new AppDomainSetup
{
ApplicationBase = "foo",
ApplicationName = "bar",
DisallowBindingRedirects = false,
ConfigurationFile = "baz",
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
}
static object CreateWrapped(AppDomain domain, Type type)
{
return domain.CreateInstanceFromAndUnwrap(
assemblyFile: type.Assembly.Location,
typeName: type.FullName,
ignoreCase: false,
bindingAttr: BindingFlags.Default,
binder: null,
args: new object[] { new A() },
culture: null,
activationAttributes: null
);
}
}
正如 Hans 指出的,问题出在我的 CreateSetup
函数中的 ApplicationBase
,我已在此处更正:
static AppDomainSetup CreateSetup()
{
return new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
ApplicationName = "bar",
DisallowBindingRedirects = false,
ConfigurationFile = "baz",
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
}
通过此修改,示例程序可以在其子域中实例化 B
和 C
的情况下正确执行。
此更改后我执行的强制程序集加载不是必需的,但我仍然无法解释为什么子应用程序域在首次构造 [=16] 后无法解析 C
构造函数=] 对象加载所需的程序集。
我最好的解释尝试是,将默认值 load context 与为其基指定了无效路径的应用程序域一起使用会导致 C
.[=19 的探测时间解析失败=]
我有一个场景,我想实现父 AppDomain
和它的子之间的简单通信。我正在尝试做的事情看起来会奏效,但我遇到了一个障碍,下面的代码片段就是一个例子。
程序导致消息未找到 C::.ctor(System.MarshalByRefObject) 的构造函数,因此看起来运行时已拒绝接受参数的构造函数我传递给 CreateInstanceFromAndUnwrap
调用的类型。我可以理解为什么实例化 B
有效并且我希望 C
表现相同,那为什么不呢?
谢谢!
编辑:在 Hans 指出我的第一个示例中的一个普通错误之后,我对下面的示例代码进行了小幅更新。因此,虽然我们的子应用程序域上仍然有一个虚构的 ApplicationBase
属性,但我们可以看到加载了正确的程序集,并且可以看到我们尝试调用的构造函数。
Hans 的观察 确实 使代码工作,但我不认为我可以将真实事物中的 ApplicationBase 设置为父域的基础。
循环第二次迭代的输出现在如下:
C
初始加载:mscorlib
展开后:mscorlib
展开后:Foo
虚空.ctor(A)
找不到类型 'C' 的构造函数。
using System;
using System.Linq;
using System.Reflection;
class A : MarshalByRefObject { }
class B : MarshalByRefObject
{
public B(MarshalByRefObject obj) { }
}
class C : MarshalByRefObject
{
public C(A obj) { }
}
static class Program
{
static void Main(string [] args)
{
foreach (var type in new [] { typeof(B), typeof(C) })
{
try
{
Console.WriteLine(type.FullName);
var setup = CreateSetup();
var domain = AppDomain.CreateDomain("foo", AppDomain.CurrentDomain.Evidence, setup);
foreach (var ass in domain.GetAssemblies())
{
Console.WriteLine("Initially Loaded: {0}", ass.GetName().Name);
}
domain.CreateInstanceFromAndUnwrap(typeof(A).Assembly.Location, typeof(A).FullName);
foreach (var ass in domain.GetAssemblies())
{
Console.WriteLine("After Unwrap: {0}", ass.GetName().Name);
}
var constructors = domain.GetAssemblies()
.ToList()
.SelectMany(ass => ass.DefinedTypes)
.First(t => t.Name == "C")
.GetConstructors();
foreach (var constructor in constructors)
{
Console.WriteLine(constructor);
}
CreateWrapped(domain, type);
AppDomain.Unload(domain);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
static AppDomainSetup CreateSetup()
{
return new AppDomainSetup
{
ApplicationBase = "foo",
ApplicationName = "bar",
DisallowBindingRedirects = false,
ConfigurationFile = "baz",
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
}
static object CreateWrapped(AppDomain domain, Type type)
{
return domain.CreateInstanceFromAndUnwrap(
assemblyFile: type.Assembly.Location,
typeName: type.FullName,
ignoreCase: false,
bindingAttr: BindingFlags.Default,
binder: null,
args: new object[] { new A() },
culture: null,
activationAttributes: null
);
}
}
正如 Hans 指出的,问题出在我的 CreateSetup
函数中的 ApplicationBase
,我已在此处更正:
static AppDomainSetup CreateSetup()
{
return new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
ApplicationName = "bar",
DisallowBindingRedirects = false,
ConfigurationFile = "baz",
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
}
通过此修改,示例程序可以在其子域中实例化 B
和 C
的情况下正确执行。
此更改后我执行的强制程序集加载不是必需的,但我仍然无法解释为什么子应用程序域在首次构造 [=16] 后无法解析 C
构造函数=] 对象加载所需的程序集。
我最好的解释尝试是,将默认值 load context 与为其基指定了无效路径的应用程序域一起使用会导致 C
.[=19 的探测时间解析失败=]