我正在编写一个Unity游戏,您在其中编写C#/统一代码(MonoBehaviours)解决问题。我将用户代码编译为dll并将其加载到主appDomain中。效果很好,但问题是添加的dll会一直保留到游戏中,直到关闭。
我找到的解决方案是将这些程序集加载到单独的appDomain中,我对此进行了管理。如果我留在子appDomain上,则无法将那些monoBehaviours附加到gameObject上。我收到错误消息:“ AddComponent询问不是Unity引擎类型的“ {type}”。否则,显然我无法将类型数据发送回主appDomain,因为它将导致程序集加载到主appDomain中。这也会发生,然后我尝试实例化一个简单的可序列化类并将其发送到主域。
统一版本为2019.3.0a6个人。
为了进行程序集的远程加载,我编译了Loader.dll并将其放置在项目的Assets文件夹中,以便统一识别它。
这是装载机,也是我尝试过的大多数事情。我尝试添加和获取的Mono与Loader在同一程序集中:
public class Loader : MarshalByRefObject
{
public void LoadBytes(byte[] bytes) /// Works
{
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
this.LogTypes(types);
}
public void GetComponentFromAttachedObject() ///ArgumentException: GetComponent requires that the requested component '{type}' derives from MonoBehaviour or Component or is an interface.
{
var main = GameObject.Find("Main");
var scr = main.GetComponent<LoaderAssemblyMonoTest>();
scr.Work();
}
public void CreatePrimitiveAndAttach() /// AddComponent asking for "{type}" which is not a Unity engine type.
{
var prim = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Debug.Log("created primitive");
prim.transform.position = new Vector3(10,10,10);
Debug.Log("modified primitive");
var scr = prim.AddComponent<LoaderAssemblyMonoTest>();
Debug.Log("attached scr to primitive");
}
public byte[] SerializeInstance(byte[] bytes) /// Loads the assembly in main domain
{`enter code here`
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
var instance = (IPlugIn)Activator.CreateInstance(types[0]);
var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, instance);
return stream.ToArray();
}
public IPlugIn[] LoadPlugins(byte[] bytes) // Loads the assembly in main domain
{
var result = new List<IPlugIn>();
var assemb = Assembly.Load(bytes);
var types = assemb.GetTypes();
foreach (var type in types)
{
if (typeof(IPlugIn).IsAssignableFrom(type))
{
result.Add((IPlugIn)Activator.CreateInstance(type));
}
}
foreach (var plugin in result)
{
Debug.Log("From Loader Domain");
plugin.DoAThing();
}
Debug.Log("After Do A Thing Loader Domain");
return result.ToArray();
}
public void AttachTest() /// AddComponent asking for "{type}" which is not a Unity engine type.
{
var main = GameObject.Find("Main");
main.AddComponent<LoaderAssemblyMonoTest>();
}
public void AttachTestMadTest1(Type type) /// AddComponent asking for "{type}" which is not a Unity engine type.
{
Debug.Log("Here");
var main = GameObject.Find("Main");
main.AddComponent(type);
}
}
这是我设置新域的方法:
private static AppDomain SetUpAsMainWithLoaderOptimization(string name, LoaderOptimization optimization)
{
var info = AppDomain.CurrentDomain.SetupInformation;
info.LoaderOptimization = optimization;
var domain = AppDomain.CreateDomain(
name,
AppDomain.CurrentDomain.Evidence,
info);
return domain;
}
这是我向其添加程序集的方式:
public static void AddAllCurrentDomainAssembliesToDomain(AppDomain domain)
{
foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())
{
domain.Load(File.ReadAllBytes(ass.Location));
}
}
这就是我实例化加载程序的方式。我不首先加载程序集,因为使用先前的方法已经存在。
private static Loader InitLoader(AppDomain domain)
{
//domain.Load(typeof(Loader).Assembly.FullName);
Loader loader = (Loader)Activator.CreateInstance(
domain,
typeof(Loader).Assembly.FullName,
typeof(Loader).FullName,
false,
BindingFlags.Public | BindingFlags.Instance,
null,
null,
null,
null).Unwrap();
return loader;
}
这是我如何使用它的一个示例:
public static void AttachMonoFromSeparateDomain ()
{
userDomain = SetUpAsMainWithLoaderOptimization("testy", LoaderOptimization.MultiDomain);
AddAllCurrentDomainAssembliesToDomain(userDomain);
userDomain.AssemblyResolve += AssemblyResolveSeparateDomain;
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveMainDomain;
var main = GameObject.Find("Main");
main.AddComponent<LoaderAssemblyMonoTest>();
var loader = InitLoader(userDomain);
loader.GetComponentFromAttachedObject();
}
根据我的阅读,我期望跨appDomains发送可序列化的类不应导致将程序集加载到主appDomain中,但可以。任何关于如何做到这一点的想法都将不会受到内存泄漏或从子appDomain附加monoBehaviours的想法,将不胜感激!