![](/img/trans.png)
[英]AppDomain.Load() throws System.IO.FileNotFoundException
[英]AppDomain.Load() fails with FileNotFoundException
我正在嘗試將我的插件dll加載到單獨的AppDomain中,但Load()方法因FileNotFoundException而失敗。 而且,似乎設置AppDomainSetup的PrivateBinPath屬性沒有任何效果,因為在日志中我看到“Initial PrivatePath = NULL”。 所有插件都有很強的名字。 通常,每個插件都存儲在[Application startp path] \\ postplugins \\ [plugindir]中 。 如果我將plugins子目錄放在[Application startp path]目錄下,一切正常。 我也試圖手動更改AppBase屬性但它沒有改變。
這是代碼:
public void LoadPostPlugins(IPluginsHost host, string pluginsDir)
{
_Host = host;
var privatePath = "";
var paths = new List<string>();
//build PrivateBinPath
var dirs = new DirectoryInfo(pluginsDir).GetDirectories();
foreach (var d in dirs)
{
privatePath += d.FullName;
privatePath += ";";
}
if (privatePath.Length > 1) privatePath = privatePath.Substring(0, privatePath.Length - 1);
//create new domain
var appDomainSetup = new AppDomainSetup { PrivateBinPath = privatePath };
Evidence evidence = AppDomain.CurrentDomain.Evidence;
var sandbox = AppDomain.CreateDomain("sandbox_" + Guid.NewGuid(), evidence, appDomainSetup);
try
{
foreach (var d in dirs)
{
var files = d.GetFiles("*.dll");
foreach (var f in files)
{
try
{
//try to load dll - here I get FileNotFoundException
var ass = sandbox.Load(AssemblyName.GetAssemblyName(f.FullName));
var f1 = f;
paths.AddRange(from type in ass.GetTypes()
select type.GetInterface("PluginsCore.IPostPlugin")
into iface
where iface != null
select f1.FullName);
}
catch (FileNotFoundException ex)
{
Debug.WriteLine(ex);
}
}
}
}
finally
{
AppDomain.Unload(sandbox);
}
foreach (var plugin in from p in paths
select Assembly.LoadFrom(p)
into ass
select
ass.GetTypes().FirstOrDefault(t => t.GetInterface("PluginsCore.IPostPlugin") != null)
into type
where type != null
select (IPostPlugin)Activator.CreateInstance(type))
{
plugin.Init(host);
plugin.GotPostsPartial += plugin_GotPostsPartial;
plugin.GotPostsFull += plugin_GotPostsFull;
plugin.PostPerformed += plugin_PostPerformed;
_PostPlugins.Add(plugin);
}
}
這是日志:
'FBTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'D:\VS2010Projects\PNotes - NET\pnfacebook\FBTest\bin\Debug\postplugins\pnfacebook\pnfacebook.dll', Symbols loaded.
A first chance exception of type 'System.IO.FileNotFoundException' occurred in FBTest.exe
System.IO.FileNotFoundException: Could not load file or assembly 'pnfacebook, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9e2a2192d22aadc7' or one of its dependencies. The system cannot find the file specified.
File name: 'pnfacebook, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9e2a2192d22aadc7'
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
at System.AppDomain.Load(AssemblyName assemblyRef)
at PNotes.NET.PNPlugins.LoadPostPlugins(IPluginsHost host, String pluginsDir) in D:\VS2010Projects\PNotes - NET\pnfacebook\FBTest\PNPlugins.cs:line 71
=== Pre-bind state information ===
LOG: User = ANDREYHP\Andrey
LOG: DisplayName = pnfacebook, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9e2a2192d22aadc7
(Fully-specified)
LOG: Appbase = file:///D:/VS2010Projects/PNotes - NET/pnfacebook/FBTest/bin/Debug/
LOG: Initial PrivatePath = NULL
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Post-policy reference: pnfacebook, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9e2a2192d22aadc7
LOG: Attempting download of new URL file:///D:/VS2010Projects/PNotes - NET/pnfacebook/FBTest/bin/Debug/pnfacebook.DLL.
LOG: Attempting download of new URL file:///D:/VS2010Projects/PNotes - NET/pnfacebook/FBTest/bin/Debug/pnfacebook/pnfacebook.DLL.
LOG: Attempting download of new URL file:///D:/VS2010Projects/PNotes - NET/pnfacebook/FBTest/bin/Debug/pnfacebook.EXE.
LOG: Attempting download of new URL file:///D:/VS2010Projects/PNotes - NET/pnfacebook/FBTest/bin/Debug/pnfacebook/pnfacebook.EXE.
當您以這種方式將程序集加載到AppDomain時,它是用於查找程序集的當前 AppDomain的PrivateBinPath。
對於您的示例,當我將以下內容添加到我的App.config時,運行正常:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="[PATH_TO_PLUGIN]"/>
</assemblyBinding>
</runtime>
這對你來說並不是很有用。
我所做的是創建一個包含IPostPlugin和IPluginsHost接口的新程序集,還有一個名為Loader的類,如下所示:
public class Loader : MarshalByRefObject
{
public IPostPlugin[] LoadPlugins(string assemblyName)
{
var assemb = Assembly.Load(assemblyName);
var types = from type in assemb.GetTypes()
where typeof(IPostPlugin).IsAssignableFrom(type)
select type;
var instances = types.Select(
v => (IPostPlugin)Activator.CreateInstance(v)).ToArray();
return instances;
}
}
我將新程序集保留在應用程序根目錄中,並且它不需要存在於插件目錄中(它可以但不會被使用,因為將首先搜索應用程序根目錄)。
然后在主AppDomain中我改為:
sandbox.Load(typeof(Loader).Assembly.FullName);
Loader loader = (Loader)Activator.CreateInstance(
sandbox,
typeof(Loader).Assembly.FullName,
typeof(Loader).FullName,
false,
BindingFlags.Public | BindingFlags.Instance,
null,
null,
null,
null).Unwrap();
var plugins = loader.LoadPlugins(AssemblyName.GetAssemblyName(f.FullName).FullName);
foreach (var p in plugins)
{
p.Init(this);
}
_PostPlugins.AddRange(plugins);
所以,我創建了眾所周知的裝載機類型的實例,然后得到那個從插件的AppDomain 中創建的插件實例。 這樣就可以按照您的意願使用PrivateBinPaths。
另一件事,私有bin路徑可以是相對的,而不是添加d.FullName
你可以添加pluginsDir + Path.DirectorySeparatorChar + d.Name
以保持最終路徑列表簡短。 這只是我個人的偏好! 希望這可以幫助。
非常感謝DedPicto和James Thurley; 我能夠實現一個完整的解決方案,我發布在這篇文章中 。
我和Emil Badh有同樣的問題:如果你試圖從“Loader”類返回一個代表當前AppDomain中未知的具體類的接口,你會得到一個“序列化異常”。
這是因為具體類型試圖反序列化。 解決方案:我能夠從“Loader”類返回一個具體類型的“自定義代理”並且它可以工作。 有關詳情,請參閱參考帖子
// Our CUSTOM PROXY: the concrete type which will be known from main App
[Serializable]
public class ServerBaseProxy : MarshalByRefObject, IServerBase
{
private IServerBase _hostedServer;
/// <summary>
/// cstor with no parameters for deserialization
/// </summary>
public ServerBaseProxy ()
{
}
/// <summary>
/// Internal constructor to use when you write "new ServerBaseProxy"
/// </summary>
/// <param name="name"></param>
public ServerBaseProxy(IServerBase hostedServer)
{
_hostedServer = hostedServer;
}
public string Execute(Query q)
{
return(_hostedServer.Execute(q));
}
}
可以返回此代理並使用,就像它是真正的具體類型一樣!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.