2013-05-03 52 views
7

Tôi đang cố tải dll plugin của mình vào AppDomain riêng biệt, nhưng phương thức Load() không thành công với FileNotFoundException. Hơn nữa, nó có vẻ như thiết lập tài sản PrivateBinPath của AppDomainSetup không có hiệu lực, bởi vì trong log tôi thấy "Initial PrivatePath = NULL". Tất cả các plugin đều có tên mạnh. Thông thường, mỗi plugin được lưu trữ trong [Đường dẫn khởi động ứng dụng] \ postplugins \ [plugindir]. Nếu tôi đặt các thư mục con plugin dưới [Đường dẫn khởi động ứng dụng] thư mục, mọi thứ đều hoạt động. Tôi cũng đã cố gắng thay đổi thuộc tính AppBase bằng tay nhưng nó không thay đổi.
Đây là mã:AppDomain.Load() không thành công với FileNotFoundException

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

Và đây là nhật ký:

'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. 

Trả lời

14

khi bạn nạp một assembly vào AppDomain theo cách đó, nó là hiện PrivateBinPath AppDomain rằng được sử dụng để tìm lắp ráp.

Ví dụ, khi tôi được thêm vào sau để App.config của tôi nó chạy tốt:

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

Đây không phải là rất hữu ích cho bạn mặc dù.

Những gì tôi đã làm thay là tạo ra một hội đồng mới có chứa các giao diện IPostPlugin và IPluginsHost, và cũng là một lớp được gọi là Loader trông như thế này:

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

tôi giữ cho rằng lắp ráp mới trong thư mục gốc ứng dụng, và nó không cần phải tồn tại trong các thư mục plugin (nó có thể nhưng sẽ không được sử dụng như là gốc ứng dụng sẽ được tìm kiếm đầu tiên).

Sau đó, trong AppDomain chính tôi đã làm điều này thay vì:

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

Vì vậy, tôi tạo ra một thể hiện của loại Loader được biết đến, và sau đó nhận được rằng để tạo ra các trường hợp plugin từ trong plugin AppDomain . Bằng cách đó, PrivateBinPaths được sử dụng theo ý muốn của bạn.

Một điều khác, đường dẫn bin riêng có thể tương đối so với việc thêm d.FullName bạn có thể thêm pluginsDir + Path.DirectorySeparatorChar + d.Name để giữ danh sách đường dẫn cuối cùng ngắn. Đó chỉ là sở thích cá nhân của tôi mặc dù! Hi vọng điêu nay co ich.

+0

James, cảm ơn bạn rất nhiều! Cuối cùng tôi thấy lời giải thích rõ ràng về việc sử dụng MarshalByRefObject. Thật không may, tôi không có đủ danh tiếng để đánh dấu câu trả lời là hữu ích (cần ít nhất 15) :( – dedpichto

+0

Không có vấn đề gì! Rất vui khi được giúp :) –

+0

Khi tôi thử điều này, mặc dù tôi đã thực hiện phương pháp LoadPlugins-generic, tôi nhận được một SerializationException. Bất kỳ đề xuất nào về lý do tại sao điều đó có thể là? Tôi có thể thấy rằng hội đồng đã được tải trong miền ứng dụng sandbox. –

0

Cảm ơn rất nhiều đến DedPicto và James Thurley; Tôi đã có thể triển khai giải pháp hoàn chỉnh, tôi đã đăng trong this post.

Tôi đã gặp vấn đề tương tự như Emil Badh: nếu bạn cố gắng trở về từ lớp "Loader" một giao diện đại diện cho một lớp cụ thể chưa được biết trong AppDomain hiện tại, bạn sẽ nhận được "Ngoại lệ nối tiếp".

Đó là do loại bê tông cố gắng được deserialized. Giải pháp: Tôi đã có thể trở về từ lớp "Loader" một loại cụ thể của một "proxy tùy chỉnh" và nó hoạt động. Xem bài đăng được tham chiếu để biết chi tiết:

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

} 

Proxy này có thể được trả lại và sử dụng như thể loại bê tông thực sự!