我有一个代码片段,用脚本引擎编译脚本,然后将程序集作为字节数组进行检索。
现在我想在沙箱中加载此Assembly
,这就是我所拥有的:
Assembly _dynamicAssembly;
ScriptEngine _engine;
Session _session;
public string Execute(string code)
{
// Setup sandbox
var e = new Evidence();
e.AddHostEvidence(new Zone(SecurityZone.Internet));
var ps = SecurityManager.GetStandardSandbox(e);
var setup = new AppDomainSetup
{ ApplicationBase = Environment.CurrentDirectory };
var domain =
AppDomain.CreateDomain("Sandbox",
AppDomain.CurrentDomain.Evidence, setup, ps);
AppDomain.CurrentDomain.AssemblyResolve += DomainAssemblyResolve;
// Process code
var submission = _engine.CompileSubmission<object>(code, _session);
submission.Compilation.Emit(memoryStream);
var assembly = memoryStream.ToArray();
_dynamicAssembly = Assembly.Load(assembly);
var loaded = domain.Load(assembly);
// Rest of the code...
}
这是AssemblyResolve的事件处理程序:
Assembly DomainAssemblyResolve(object sender, ResolveEventArgs args)
{
return _dynamicAssembly;
}
这意味着当我domain.Load(assembly)
时,我将获得_dynamicAssembly,如果我没有订阅该事件并返回Assembly
,我会得到FileNotFoundException
。
以上编译并运行,但问题是在域程序集中执行的代码实际上并未在沙箱中执行。当我获得submission-method并在其中调用工厂并返回此AppDomain.CurrentDomain.FriendlyName
时,结果是:MyRoslynApplication.vshost.exe
不沙箱AppDomain
我加载byte[]
- 汇编错了吗?
答案 0 :(得分:8)
如果要加载在沙箱中运行的类型并从主AppDomain访问它,则需要使用CreateInstanceFromAndUnwrap之类的方法。该类型需要是MarshalByRefObject,以便它可以在调用AppDomain中创建透明代理以进行访问。
如果主AppDomain解析程序集,它将被加载到主AppDomain(以及沙箱AppDomain)中,以便最终加载两个副本。您的主AppDomain必须始终通过代理与沙箱隔离,以便只能访问MarshalByRefObject和可序列化对象。请注意,您要引用的类型也无法在要加载到沙箱中的程序集中定义;你需要在第三个通用程序集中定义接口和可序列化的类型,然后将其加载到主和Appbox域中。
我已经做了一些额外的挖掘,看起来所有将程序集加载到另一个AppDomain并生成代理的方法都需要解析程序集名称。我不确定在这种情况下是否可以通过byte []加载;您可能需要将程序集保存到磁盘并加载它。我会再多挖一点。
我认为你可以这样做(这是未经测试的,但似乎有道理)。
这些需要位于主界面和沙盒都可访问的“界面”程序集中(我将其称为Services.dll):
public interface IMyService
{
//.... service-specific methods you'll be using
}
public interface IStubLoader
{
Object CreateInstanceFromAndUnwrap(byte[] assemblyBytes, string typeName);
}
接下来是StubLoader.dll中的一个类。你不会直接引用这个程序集;这是你将调用第一个AppDomain.CreateInstanceFromAndUnwrap的地方,将其作为程序集名称和StubLoader作为类型名称。
public sealed class StubLoader: MarshalByRefObject, IStubLoader
{
public object CreateInstanceFromAndUnwrap(byte[] assemblyBytes, string typeName)
{
var assembly = Assembly.Load(assemblyBytes);
return assembly.CreateInstance(typeName);
}
}
现在,要从主AppDomain中使用它,请执行以下操作:
//Create transparent proxy for the stub loader, which will live in the sandbox
var stubLoader = (IStubLoader)sandboxDomain.CreateInstanceFromAndUnwrap("Stubloader.dll", "StubLoader");
//Have the stub loader marshal a proxy to a dynamically loaded assembly (via byte[]) where MyService is the type name implementing MarshalByRefObject and IMyService
var myService = (IMyService)stubLoader.CreateInstanceFromAndUnwrap(assemblyBytes, "MyService");
不幸的是,AppDomains使用起来并不简单。这是因为它们提供高度绝缘,因此需要代理以允许跨AppDomain边界使用。
为了回应您如何编组非可序列化和非MarshalByRefObject类,这里有一个关于共享接口DLL中可能包含的内容的粗略示例:
public interface ISessionWrapper
{
void DoSomethingWithSession();
}
public sealed class SessionWrapper : MarshalByRefObject, ISessionWrapper
{
private readonly Session _session;
public SessionWrapper(Session session)
{
_session = session;
}
public void DoSomethingWithSession()
{
//Do something with the wrapped session...
//This executes inside the sandbox, even though it can be called (via proxy) from outside the sandbox
}
}
现在,无论您在原始服务处理Session所需的任何地方,它都可以通过ISessionWrapper,其调用将在幕后进行编组,以便所有实际代码都在沙盒中的沙盒中实现。 / p>
答案 1 :(得分:0)
可能这也有帮助:
此外,我必须将创建的程序集文件流保留在磁盘上以使Sandbox正常工作。我仍然在努力寻求解决方案的强命名和单元测试(有超过16个项目),但是,我会用工作副本回复你。