从内存中将程序集加载到 Appdomain,createinstance 导致 'filenotfound' 异常

Loaded an assembly into Appdomain from memory, createinstance results in 'filenotfound' exception

对于某些上下文:我已经 运行 在第三方应用程序提供的 "plugin" 应用程序域中。这很重要,因为它限制了类加载时可以做的事情。更具体地说,此应用程序依赖于某些预定义的核心 dll 位于经典应用程序基本路径上,并从数据库动态加载其他所有内容。虽然我没有看到那个层,但它似乎没有使用文件系统(临时文件之类的东西)所以我假设它正在将所有非核心的 DLL 作为字节数组直接加载到内存中。

我有一个作为嵌入式引用加载的 DLL,它需要某个 app.config 和插件上下文中不可控制的 bin 目录,因此我打算使用 appdomain。插件中的 DLL 嵌入资源被读取为字节数组并注入到应用程序域中。

DLL 出现在创建的应用程序域的 AppDomain.GetAssemblies() 中,但是,当我尝试从该 DLL 创建实例时,它抱怨 "FileNotFound"。我检查了 FullNames 是否匹配等。这提出了第一个也是最简单的问题:为什么要寻找已经加载的 DLL?它似乎是为了查找类型而故意跳过所有字节数组加载的程序集。我试过禁用基本路径探测,这只是将错误从 FileNotFound 更改为更通用的引用未找到错误。这是尝试使用内存注入 DLL 并失败的代码。

public AdsAdapter(Context context)
        {   

            try
            {
                var adsPath = session.GetSystemSetting("SpecialConfig");
                var adsBin = Path.Combine(session.GetSystemSetting("SpecialBin"), @"special\path");

                AppDomainSetup appSetup = new AppDomainSetup()
                {
                    ApplicationName = "ExtendsProxyOC",
                    ApplicationBase = adsBin,
                    PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory,
                    ConfigurationFile = System.IO.Path.Combine(adsPath,"cs_client_app.config")
                };

                _adsDomain = AppDomain.CreateDomain("adsDomain"+Guid.NewGuid(),
                    null,appSetup);

                // add inmemory DLL

                var dll = Assembly.GetAssembly(typeof(AdsAdapter))
                       .GetManifestResourceStream(resourcespath + "ADSOCWrapper.dll");
                var dlldbg = Assembly.GetAssembly(typeof(AdsAdapter))
                    .GetManifestResourceStream(resourcespath + "ADSOCWrapper.pdb");
                Assembly asmWrapper = _adsDomain.Load(StreamToBytes(dll), StreamToBytes(dlldbg));

                 // Load a file which IS in the bin directory filesystem
                _adsDomain.Load("ads_core");

                // the DLL IS in memory  at this point and this proves it. Type definitions are there, looks fine.
                Assembly[] assm = _adsDomain.GetAssemblies();

                // error occurs here "FileNotFound" in the app base for the .DLL file. Why is it trying to load from disk when it is already loaded?
                _proxy = (AdsRemote)_adsDomain.CreateInstanceFromAndUnwrap("ADSOCWrapper",
                    typeof(AdsRemote).FullName, 
                    true, //case search
                    System.Reflection.BindingFlags.CreateInstance, //bindingattr
                    null, //Binder
                    new object[]{}, //args
                    null, //culture
                    null);//activationattr


            }
            catch (Exception e)
            {
                throw e;
            }
        }

        static byte[] StreamToBytes(Stream input)
        {
            var capacity = input.CanSeek ? (int)input.Length : 0;
            using (var output = new MemoryStream(capacity))
            {
                int readLength;
                var buffer = new byte[4096];

                do
                {
                    readLength = input.Read(buffer, 0, buffer.Length);
                    output.Write(buffer, 0, readLength);
                }
                while (readLength != 0);

                return output.ToArray();
            }
        }

}

我的下一步是尝试覆盖程序集解析过程,我相信这可能是答案,但由于我没有文件系统 dll 'anchor' 看来我无法 bootstrap我的自定义代码。

即使在我的 AppDomainSetup.AppDomainInitializer 中使用匿名委托函数,它显然也依赖于其他一些可用的 DLL。我只在我的委托中调用 GAC 对象,但它需要将执行程序集(设置应用程序域的程序集)加载到目标应用程序域中。为了传递一个匿名委托。为什么?

Could not load file or assembly 'ParentAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

AppDomainInitializer = new AppDomainInitializer(delegate(string[] target)
                            {
                                AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
                                {
                                    if (args.Name.Contains("ADSOCWrapper"))
                                    {
                                        return Array.Find(AppDomain.CurrentDomain.GetAssemblies(),
                                            x => x.FullName.Contains("ADSOCWrapper"));
                                    }
                                    return null;
                                };
                            })

因此,我似乎无法让我的应用程序域在内存中查找其类型。这里有什么想法吗?

您使用的 CreateInstanceFromAndUnwrap 重载采用程序集文件而不是程序集名称。

尝试以 assembly name 作为参数的 CreateInstanceFromAndUnwrap 重载:

       _proxy = (AdsRemote)_adsDomain.CreateInstanceFromAndUnwrap("ADSOCWrapper",
                typeof(FeedDao).FullName);