我有一些代码用于将程序集加载到沙盒的AppDomain中(PermissionSet初始化为PermissionState.None)。我想将数据从我的主AppDomain来回传递到沙盒AppDomain中的程序集。通过使我的数据扩展MarshalByRefObject,它可以通过引用传递。问题是这些对象然后在内存中徘徊。我希望尽快收集它们(在大多数情况下在几秒钟内)。当我尝试覆盖InitializeLifetimeService时,我收到运行时错误。
我已经汇总了一些演示此问题的示例代码。我的代码分为三个项目:
类库:沙箱(必须签名)
文件名:Sandbox.cs (基于this stackoverflow answer)
using System;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
namespace Sandbox
{
public class Sandbox : MarshalByRefObject
{
const string BaseDirectory = "Untrusted";
const string DomainName = "Sandbox";
Assembly assembly;
public Sandbox()
{
}
public static Sandbox Create()
{
var setup = new AppDomainSetup()
{
ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
ApplicationName = DomainName,
DisallowBindingRedirects = true,
DisallowCodeDownload = true,
DisallowPublisherPolicy = true
};
var permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());
return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
}
public void LoadAssembly(string assemblyPath)
{
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
this.assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
}
public object CreateInstance(string typeName)
{
Type type = this.assembly.GetType(typeName);
return Activator.CreateInstance(type);
}
public object InvokeStaticMethod(string typeName, string methodName, params object[] parameters)
{
Type type = this.assembly.GetType(typeName);
return type.GetMethod(methodName).Invoke(null, parameters);
}
public override object InitializeLifetimeService()
{
return null; // sandboxes don't expire
}
}
}
类库:插件
文件名:Plugin.cs
namespace Plugins
{
public static class Plugin
{
public static string DoSomething(SharedData data)
{
data.Set("updated", "yes");
return data.Get("name");
}
}
}
文件名:SharedData.cs
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Lifetime;
namespace Plugins
{
public class SharedData : MarshalByRefObject
{
Dictionary<string, string> dict = new Dictionary<string, string>();
public override object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
lease.RenewOnCallTime = TimeSpan.FromMinutes(1);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(1);
}
return lease;
}
public void Set(string key, string value)
{
this.dict[key] = value;
}
public string Get(string key)
{
if (this.dict.ContainsKey(key))
{
return this.dict[key];
}
return null;
}
}
}
控制台项目:示例
文件名:Program.cs
using System;
using System.IO;
namespace Example
{
class Program
{
static void Main(string[] args)
{
// create sandbox domain
var sandbox = Sandbox.Sandbox.Create();
// load a "plugin" assembly into it
string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Plugins.dll");
sandbox.LoadAssembly(assemblyPath);
// create some data in the sandbox
var data = (Plugins.SharedData)sandbox.CreateInstance("Plugins.SharedData");
data.Set("name", "Nogwater");
// pass some data into the plugin
object objRet = sandbox.InvokeStaticMethod("Plugins.Plugin", "DoSomething", data);
// get updates back from the plugin
string updated = data.Get("updated");
Console.WriteLine("{0} {1}", objRet, updated);
}
}
}
错误发生在调用Sandbox.CreateInstance
的行的this.assembly.GetType()
中。我得到的信息是:
覆盖成员时违反了继承安全规则:&#39; Plugins.SharedData.InitializeLifetimeService()&#39;。覆盖方法的安全性可访问性必须与要覆盖的方法的安全性可访问性相匹配。
似乎InitializeLifetimeService需要比我想要给不受信任的插件更多的安全性。如果我在Plugins库之外定义SharedData类并尝试传入它(一旦它试图通过引用封送对象),我会收到类似的错误。
有没有办法让这项工作无需借助于按值复制和序列化?
编辑:仅供参考,我最终使用了序列化。这很痛苦,特别是对于数据发生变化的地方而且我必须再次序列化以返回它,但至少它是有效的。我很想知道是否有人可以通过编组工作。