2013-07-24 38 views
5

Tôi đang thử nghiệm với việc tải một assembly bằng cách sử dụng các mảng byte, nhưng tôi không thể tìm ra cách để nó hoạt động đúng cách. Đây là thiết lập:Đang tải Assembly Byte Array

public static void Main() 
{ 
    PermissionSet permissions = new PermissionSet(PermissionState.None); 
    AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
    AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

    Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
    Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

    // Crashes here saying it can't find the file. 
    friendlyDomain.Load(dependency); 

    AppDomain.Unload(friendlyDomain); 

    Console.WriteLine("Stand successful"); 
    Console.ReadLine(); 
} 

Tôi đã tạo hai dlls giả và đổi tên thành tiện ích mở rộng thành '.dll_' để hệ thống không thể tìm thấy tệp thực. Cả hai primarydependency điền chính xác, nhưng khi tôi cố gắng gọi phương thức AppDomain.Load với dữ liệu nhị phân, nó trở lại với:

Could not load file or assembly 'Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Tại sao nó sẽ được tìm kiếm hệ thống cho một tập tin?

CẬP NHẬT

này mặt khác dường như làm việc:

public class Program { 
    public static void Main() { 
     PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); 
     AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
     AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

     Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
     Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

     // Crashes here saying it can't find the file. 
     // friendlyDomain.Load(primary); 

     Stage stage = (Stage)friendlyDomain.CreateInstanceAndUnwrap(typeof(Stage).Assembly.FullName, typeof(Stage).FullName); 
     stage.LoadAssembly(dependency); 

     Console.WriteLine("Stand successful"); 
     Console.ReadLine(); 
    } 

} 

public class Stage : MarshalByRefObject { 
    public void LoadAssembly(Byte[] data) { 
     Assembly.Load(data); 
    } 
} 

Vì vậy, nó dường như có sự khác biệt giữa AppDomain.LoadAssembly.Load.

+0

Liệu DLL phụ thuộc có bất kỳ phụ thuộc nào chưa được sao chép, có lẽ không? –

+0

Tiểu học phụ thuộc vào sự phụ thuộc. Mặc dù phụ thuộc không có phụ thuộc (non-CLR). Dường như thời gian chạy không nên tìm kiếm tệp để bắt đầu. – sircodesalot

Trả lời

9

này là bình thường, CLR không xem xét "phụ thuộc" bạn nạp được một hội đồng phù hợp khi nó tìm kiếm cho lắp ráp rằng "chính" cần. Một vấn đề liên quan đến "tải ngữ cảnh", không có một cho hội đồng nạp như thế này. Đây là cố ý, CLR không thể đảm bảo rằng địa ngục DLL sẽ không phải là một vấn đề vì nó không có ý tưởng nơi hội đồng đến từ. Kể từ khi bạn mở cửa cho DLL Hell, bạn cũng phải tránh địa ngục mình.

Bạn cần triển khai sự kiện AppDomain.AssemblyResolve. Nó sẽ kích hoạt khi CLR không thể tìm thấy "dependency", bạn có thể trả về assembly mà bạn nhận được từ Assembly.Load (byte []). Tuy nhiên, bạn sẽ phải làm như vậy một cách nhất quán khi nó bắn nhiều hơn một lần cho cùng một hội đồng, nói cách khác trả về chính xác cùng một hội, hoặc bạn sẽ có nhiều vấn đề gây ra bởi nhận dạng loại .NET. Sản xuất khó hiểu ngoại lệ truyền, "không thể truyền Foo đến Foo".

Có các vấn đề khác, điều này khá không hiệu quả. Bộ nhớ ảo cho hội đồng không thể được hỗ trợ bởi một tập tin trên đĩa do đó nó được hỗ trợ bởi tệp hoán trang. Mà làm tăng kích thước cam kết cho quá trình của bạn.

Tốt hơn hết là không nên làm điều này.

+0

Tại sao 'Assembly.Load' dường như cho phép điều này, trong khi' AppDomain.Load' thì không? Ngoài ra, điểm tốt về kích thước cam kết. – sircodesalot

+0

Nó không có liên quan gì đến Assembly.Load. Khi bạn sử dụng AssemblyResolve, CLR nói "Tôi cần điều này" và bạn nói "ở đây là". Vì vậy, CLR biết những gì nó có. Cách tiếp cận ban đầu của bạn là "Đây là một cái gì đó" và CLR nói "không biết đó là cái gì". –

+0

Tôi làm theo những gì bạn đang nói, nhưng chỉ bằng cách chuyển đổi các phương pháp có vẻ như nó hoạt động. Tôi không bao giờ phải sử dụng 'AssemblyResolve' vì CLR chấp nhận nó khi nó đến từ' Assembly.Load'. – sircodesalot

0

Nếu bạn sử dụng FusionLogViewer bạn có thể xem chi tiết hơn về các vấn đề cụ thể CLR là có trong tải một hội đồng .... nó có thể cho bạn thấy những địa điểm nó đang cố gắng thăm dò để cung cấp cho bạn một đầu mối, vv

bạn cũng có thể xử lý các sự kiện AssemblyLoad/AssemblyResolve/ResourceResolve trên AppDomain của bạn trong mã của bạn, để theo dõi thông qua các chuỗi .

Đây là một ví dụ tiện dụng mà sử dụng một MSBuild bước tùy chỉnh để nhúng bất kỳ của hội phụ thuộc dự án của bạn như tài nguyên vào chương trình EXE của bạn, và sau đó sử dụng AssemblyResolve để tải chúng từ một ResourceStream (đang thực hiện Assembly.Load() trên mảng byte []).

+0

'Nó có thể cho bạn thấy vị trí mà nó đang cố gắng thăm dò'. Đó chỉ là nó, nó không nên được thăm dò nó có vẻ như thế. Tôi đã xác định nhị phân, vì vậy không có lý do gì để nó đi ra ngoài và tải nó một lần nữa từ hệ thống tập tin. – sircodesalot

+0

'Chính' phụ thuộc vào 'Phụ thuộc', mặc dù. Việc tải 'Dependency' sẽ không gây ra bất kỳ vấn đề nào, và trên thực tế, tôi đã phát hiện ra rằng việc sử dụng' Assembly.Load' có vẻ tốt, trong khi 'AppDomain.Load' thì không. – sircodesalot

3

Không có sự khác biệt giữa hai phương pháp này (bạn có thể kiểm tra official source code nếu bạn muốn).

Trong trang MSDN cho AppDomain.Load Method (Byte[]) nó được nhận xét rằng phương pháp này đang tải lắp ráp trong lĩnh vực ứng dụng hiện tại:

Phương pháp này nên được sử dụng chỉ để nạp một assembly vào miền ứng dụng hiện tại. Phương thức này được cung cấp để thuận tiện cho những người gọi có khả năng tương tác không thể gọi phương thức tĩnh Assembly.Load . Để tải các assembly vào các miền ứng dụng khác, sử dụng phương thức như CreateInstanceAndUnwrap.

dòng:

friendlyDomain.Load(dependency); 

cư xử giống hệt nhau với:

Assembly.Load(dependency); 

Lý do nó hoạt động trong bạn mẫu mã được cập nhật, là vì đối tượng Stage là thực sự gọi Assembly.Load bên trong con AppDomain.

Lưu ý: Câu trả lời này bổ sung cho câu trả lời của Hans Passant và colinsmith.

+0

Cuối cùng tôi quyết định đi với lưu các hội đồng vào một thư mục tạm thời trên đĩa và sau đó thiết lập cơ sở 'AppDomain' vào thư mục đó. Tôi có thể điên, nhưng tôi thề có vẻ như 'AppDomain.Load' vẫn sẽ kiểm tra đĩa cho sự hiện diện vật lý của một hội đồng, trong khi' Assembly.Load' thì không. Trong thực tế, trong thực hiện hiện tại của tôi, tôi chỉ cần chuyển tên đầy đủ của assembly vào 'friendlyDomain.Load' và nó lấy nó từ' ApplicationBase' (như mong đợi).Nhưng tại sao nó sẽ không lấy mảng byte mà không cần xác minh là ngoài tôi. – sircodesalot

Các vấn đề liên quan