2013-05-03 27 views
7

プラグインdllを別のAppDomainにロードしようとしていますが、Load()メソッドがFileNotFoundExceptionで失敗します。また、AppDomainSetupのPrivateBinPathプロパティを設定しても効果はありません。ログには「Initial PrivatePath = NULL」が表示されるためです。すべてのプラグインは強力な名前を持っています。通常、各プラグインは[Application startp path] \ postplugins \ [plugindir]に格納されています。プラグインのサブディレクトリを[Application startp path]ディレクトリに置くと、すべて動作します。私はまた、AppBaseプロパティを手動で変更しようとしましたが、変更はありません。
ここではコードです:FileNotFoundExceptionでAppDomain.Load()が失敗する

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. 

答えて

14

あなたはその方法でのAppDomainにアセンブリをロードし、それが使用さ現在のAppDomainのPrivateBinPathがありますアセンブリを見つける。ご例えば

私はApp.configファイルに以下を追加したときに、それが正常に実行しました:

<runtime> 
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
    <probing privatePath="[PATH_TO_PLUGIN]"/> 
    </assemblyBinding> 
</runtime> 

をこれはしかし、あなたにとって非常に有用ではありません。私が代わりにやった

がIPostPluginとIPluginsHostインタフェースが含まれて新しいアセンブリを作成することでしたし、また、このように見えたローダーと呼ばれるクラス:

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はあなたが望むように使用されます。

プライベートビンのパスは相対的なものにすることができます。d.FullNameを追加するのではなく、pluginsDir + Path.DirectorySeparatorChar + d.Nameを追加して最終的なパスリストを短くすることができます。それはちょうど私の個人的な好みです!お役に立てれば。

+0

James、ありがとう!最後に、MarshalByRefObjectの使い方についての明確な説明があります。残念ながら、私は答えが有用であると評価するには十分な評判がありません(少なくとも15が必要です):( – dedpichto

+0

問題なし!喜んで助けました:) –

+0

これを試してみると、LoadPluginsメソッドを汎用化しましたが、SerializationExceptionが発生します。それがなぜあるのかについての示唆はありますか?アセンブリがサンドボックスアプリケーションドメインに読み込まれていることがわかります。 –

0

DedPictoとJames Thurleyに感謝します。私はthis postで投稿した完全なソリューションを実装することができました。

Emil Badhと同じ問題がありました。現在のAppDomainで未知の具体的なクラスを表すインターフェイスを "Loader"クラスから返そうとすると、 "Serialization Exception"が発生します。

コンクリートタイプが逆シリアル化されているためです。 解決策:「ローダー」クラスから具体的なタイプの「カスタムプロキシ」を返すことができました。詳細については、参照先の記事を参照してください。

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

} 

このプロキシは実際のコンクリートタイプのように返されて使用できます。

関連する問題