3 回答

TA貢獻(xiàn)1966條經(jīng)驗(yàn) 獲得超4個(gè)贊
這里的主要問題是,您可以在執(zhí)行主appdomain中的代碼時(shí)實(shí)例化插件。
您需要做的是創(chuàng)建一個(gè)代理類型,該類型在已加載的程序集中定義,但在新的appdomain中實(shí)例化。在兩個(gè)應(yīng)用程序域中都未加載類型的程序集的情況下,您無法跨應(yīng)用程序域邊界傳遞類型。 例如,如果您想像上面那樣枚舉類型并打印到控制臺,則應(yīng)從在新應(yīng)用程序域中執(zhí)行的代碼執(zhí)行,而不要從在當(dāng)前應(yīng)用程序域中執(zhí)行的代碼執(zhí)行。
所以,讓我們創(chuàng)造我們的插件的代理,這樣會(huì)存在于你的主要組件,并負(fù)責(zé)執(zhí)行所有插件相關(guān)的代碼:
// Mark as MarshalByRefObject allows method calls to be proxied across app-domain boundaries
public class PluginRunner : MarshalByRefObject
{
// make sure that we're loading the assembly into the correct app domain.
public void LoadAssembly(byte[] byteArr)
{
Assembly.Load(byteArr);
}
// be careful here, only types from currently loaded assemblies can be passed as parameters / return value.
// also, all parameters / return values from this object must be marked [Serializable]
public string CreateAndExecutePluginResult(string assemblyQualifiedTypeName)
{
var domain = AppDomain.CurrentDomain;
// we use this overload of GetType which allows us to pass in a custom AssemblyResolve function
// this allows us to get a Type reference without searching the disk for an assembly.
var pluginType = Type.GetType(
assemblyQualifiedTypeName,
(name) => domain.GetAssemblies().Where(a => a.FullName == name.FullName).FirstOrDefault(),
null,
true);
dynamic plugin = Activator.CreateInstance(pluginType);
// do whatever you want here with the instantiated plugin
string result = plugin.RunTest();
// remember, you can only return types which are already loaded in the primary app domain and can be serialized.
return result;
}
}
在這里,我將在上面的評論中重申一些要點(diǎn):
您必須繼承自MarshalByRefObject,這意味著可以使用遠(yuǎn)程處理跨應(yīng)用程序域邊界代理對此對象的調(diào)用。
將數(shù)據(jù)傳遞到代理類或從代理類傳遞數(shù)據(jù)時(shí),必須對數(shù)據(jù)進(jìn)行標(biāo)記,[Serializable]并且還必須采用當(dāng)前加載的程序集中的類型。如果您需要插件將某些特定對象返回給您,則說PluginResultModel您應(yīng)該在由兩個(gè)程序集/應(yīng)用程序域加載的共享程序集中定義此類。
必須將程序集合格的類型名傳遞給CreateAndExecutePluginResult其當(dāng)前狀態(tài),但是可以通過自己迭代程序集和類型并刪除對的調(diào)用來消除此要求Type.GetType。
接下來,您需要?jiǎng)?chuàng)建域并運(yùn)行代理:
static void Main(string[] args)
{
var bytes = File.ReadAllBytes(@"...filepath...");
var domain = AppDomain.CreateDomain("plugintest", null, null, null, false);
var proxy = (PluginRunner)domain.CreateInstanceAndUnwrap(typeof(PluginRunner).Assembly.FullName, typeof(PluginRunner).FullName);
proxy.LoadAssembly(bytes);
proxy.CreateAndExecutePluginResult("TestPlugin.Class1, TestPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
}
再說一遍,因?yàn)樗浅V匾?,而且我很長一段時(shí)間都不了解:當(dāng)您在此代理類上執(zhí)行方法時(shí),例如proxy.LoadAssembly實(shí)際上是被序列化為字符串并傳遞給新應(yīng)用程序要執(zhí)行的域。這不是正常的函數(shù)調(diào)用,您需要非常小心傳入/傳出這些方法的內(nèi)容。

TA貢獻(xiàn)1779條經(jīng)驗(yàn) 獲得超6個(gè)贊
此調(diào)用導(dǎo)致System.IO.FileNotFoundException,但我不知道為什么。我懷疑會(huì)發(fā)生異常,因?yàn)樵俅卧诖疟P上搜索了程序集。但是,為什么程序要再次加載它?
這里的關(guān)鍵是了解加載程序上下文,有關(guān)MSDN的一篇很好的文章:
將加載程序上下文視為保存程序集的應(yīng)用程序域中的邏輯存儲區(qū)。根據(jù)程序集的加載方式,它們屬于三種加載程序上下文之一。
加載上下文
LoadFrom上下文
兩種情況都沒有
從裝byte[]配件將裝配件放置在這兩個(gè)上下文中。
至于這兩個(gè)上下文,除非應(yīng)用程序預(yù)訂AssemblyResolve事件,否則不能綁定該上下文中的程序集。通常應(yīng)避免這種情況。
在下面的代碼中,我們使用AssemblyResolve事件在Load上下文中加載程序集,從而使我們能夠綁定到該程序集。
如何使用從字節(jié)數(shù)組加載的程序集在新的appdomain中創(chuàng)建實(shí)例?
請注意,這僅僅是概念的證明,它是探索裝載程序上下文的基本要素。建議的方法是使用@caesay描述的代理,并在本文中由Suzanne Cook進(jìn)一步評論。
這是一個(gè)不保留對實(shí)例的引用的實(shí)現(xiàn)(類似于即發(fā)即棄)。
首先,我們的插件:
Test.cs
namespace Plugins
{
public class Test
{
public Test()
{
Console.WriteLine($"Hello from {AppDomain.CurrentDomain.FriendlyName}.");
}
}
}
接下來,在一個(gè)新ConsoleApp的插件加載器中:
PluginLoader.cs
[Serializable]
class PluginLoader
{
private readonly byte[] _myBytes;
private readonly AppDomain _newDomain;
public PluginLoader(byte[] rawAssembly)
{
_myBytes = rawAssembly;
_newDomain = AppDomain.CreateDomain("New Domain");
_newDomain.AssemblyResolve += new ResolveEventHandler(MyResolver);
}
public void Test()
{
_newDomain.CreateInstance("plugin", "Plugins.Test");
}
private Assembly MyResolver(object sender, ResolveEventArgs args)
{
AppDomain domain = (AppDomain)sender;
Assembly asm = domain.Load(_myBytes);
return asm;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
byte[] rawAssembly = File.ReadAllBytes(@"D:\Projects\AppDomainTest\plugin.dll");
PluginLoader plugin = new PluginLoader(rawAssembly);
// Output:
// Hello from New Domain
plugin.Test();
// Output:
// Assembly: mscorlib
// Assembly: ConsoleApp
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine($"Assembly: {asm.GetName().Name}");
}
Console.ReadKey();
}
}
CreateInstance("plugin", "Plugins.Test")盡管不了解插件程序集,但顯示的輸出已成功從默認(rèn)應(yīng)用程序域中成功調(diào)用。
- 3 回答
- 0 關(guān)注
- 231 瀏覽
添加回答
舉報(bào)