从内存中将一个程序集加载到Appdomain中,createinstance结果在' filenotfound'例外

时间:2016-02-16 18:03:07

标签: c# embedded-resource appdomain

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

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

DLL在AppDomain.GetAssemblies()中显示为创建的appdomain,但是,当我尝试从该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'好像我无法引导我的自定义代码。

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

  

无法加载文件或程序集' ParentAssembly,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'或其中一个依赖项。系统找不到指定的文件。

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;
                                };
                            })

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

1 个答案:

答案 0 :(得分:1)

您使用CreateInstanceFromAndUnwrap的重载需要汇编文件而不是汇编名称。

尝试以assembly name作为参数的 _proxy = (AdsRemote)_adsDomain.CreateInstanceFromAndUnwrap("ADSOCWrapper", typeof(FeedDao).FullName); 重载:

  // Both cause the warning
  ptr[i] += (unsigned char) 32;
  ptr[i] = tolower(ptr[i]);