在Unity中跨AppDomain共享来自已编译Dll的信息

时间:2019-07-09 10:29:17

标签: c# unity3d appdomain

我正在编写一个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的想法,将不胜感激!

0 个答案:

没有答案