2008-09-18 26 views
53

Tôi có một tình huống mà tôi có DLL mà tôi đang tạo sử dụng một DLL của bên thứ ba khác, nhưng tôi muốn có thể xây dựng bên thứ ba DLL vào DLL của tôi thay vì phải giữ chúng lại với nhau nếu có thể.Nhúng một dll vào một tài nguyên khác dưới dạng tài nguyên được nhúng và sau đó gọi nó từ mã của tôi

Điều này với C# và .NET 3.5.

Cách tôi muốn thực hiện điều này là lưu trữ DLL của bên thứ ba dưới dạng tài nguyên được nhúng mà sau đó tôi đặt ở vị trí thích hợp trong khi thực hiện DLL đầu tiên. Cách tôi dự định ban đầu để làm điều này là viết mã để đưa DLL của bên thứ ba vào vị trí được chỉ định bởi System.Reflection.Assembly.GetExecutingAssembly(). Location.ToString() trừ /nameOfMyAssembly.dll cuối cùng. Tôi có thể lưu thành công bên thứ ba .DLL tại vị trí này (kết thúc bằng (C: \ Documents and Settings \ myUserName \ Local Settings \ Application Data \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901), nhưng khi tôi nhận được đến một phần của mã của tôi đòi hỏi DLL này nó không thể tìm thấy nó.

không ai có bất kỳ ý tưởng như những gì tôi cần phải làm khác đi?

Trả lời

41

Khi bạn đã nhúng cụm của bên thứ ba làm tài nguyên, hãy thêm mã để đăng ký sự kiện AppDomain.AssemblyResolve của miền hiện tại trong khi khởi động ứng dụng. Sự kiện này kích hoạt bất cứ khi nào hệ thống con Fusion của CLR không định vị được một assembly theo các tham số (chính sách) có hiệu lực. Trong trình xử lý sự kiện cho AppDomain.AssemblyResolve, hãy tải tài nguyên bằng cách sử dụng Assembly.GetManifestResourceStream và nạp nội dung của nó dưới dạng mảng byte vào quá tải Assembly.Load tương ứng.Dưới đây là cách người ta thực hiện như vậy có thể trông giống như trong C#:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => 
{ 
    var resName = args.Name + ".dll";  
    var thisAssembly = Assembly.GetExecutingAssembly();  
    using (var input = thisAssembly.GetManifestResourceStream(resName)) 
    { 
     return input != null 
      ? Assembly.Load(StreamToBytes(input)) 
      : null; 
    } 
}; 

nơi StreamToBytes có thể được định nghĩa là:

static byte[] StreamToBytes(Stream input) 
{ 
    var capacity = input.CanSeek ? (int) input.Length : 0; 
    using (var output = new MemoryStream(capacity)) 
    { 
     int readLength; 
     var buffer = new byte[4096]; 

     do 
     { 
      readLength = input.Read(buffer, 0, buffer.Length); 
      output.Write(buffer, 0, readLength); 
     } 
     while (readLength != 0); 

     return output.ToArray(); 
    } 
} 

Cuối cùng, như một vài đã đề cập, ILMerge có thể là một lựa chọn để xem xét, mặc dù có liên quan nhiều hơn một chút.

+0

Được thực hiện sau khi đăng @dgvid đánh bại tôi trong thời gian phản hồi. : P –

+0

Tôi đã sử dụng mã này rất thành công để thực hiện chính xác những gì tôi muốn. Xem bài viết của tôi cho một vài thiếu sót cú pháp nhỏ tôi cố định (không đủ đại diện để chỉnh sửa này;)). –

+0

Đó là slick, cũng được thực hiện. – jcollum

11

có một công cụ gọi là ILMerge rằng có thể thực hiện việc này: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Sau đó, bạn chỉ có thể tạo sự kiện xây dựng tương tự như sau đây:

Đặt đường dẫn = "C: \ Program Files \ Microsoft \ ILMerge"

ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $ (ProjectDir) \ bin \ Release \ release.exe $ (ProjectDir) \ bin \ Release \ InteractLib.dll $ (ProjectDir) \ bin \ Release \ SpriteLib.dll $ (ProjectDir) \ bin \ Release \ LevelLibrary.dll

2

Thay vì viết assembly vào đĩa bạn có thể cố gắng thực hiện Assembly.Load (byte [] rawAssembly) nơi bạn tạo rawAssembly từ tài nguyên được nhúng.

9

Bạn có thể thực hiện điều này dễ dàng một cách đáng kể bằng cách sử dụng Netz, một .net NET Executables Compressor & Packer.

8

Tôi đã thành công khi làm những gì bạn mô tả, nhưng vì bên thứ ba DLL cũng là một hội đồng .NET, tôi không bao giờ ghi nó ra đĩa, tôi chỉ tải nó từ bộ nhớ.

tôi nhận được lắp ráp nguồn nhúng như một mảng byte như vậy:

 Assembly resAssembly = Assembly.LoadFile(assemblyPathName); 

     byte[] assemblyData; 
     using (Stream stream = resAssembly.GetManifestResourceStream(resourceName)) 
     { 
      assemblyData = ReadBytesFromStream(stream); 
      stream.Close(); 
     } 

Sau đó, tôi tải dữ liệu với Assembly.Load().

Cuối cùng, tôi thêm một trình xử lý vào AppDomain.CurrentDomain.AssemblyResolve để trả về assembly đã tải của tôi khi trình nạp kiểu trông nó.

Xem .NET Fusion Workshop để biết thêm chi tiết.

17

Cuối cùng, tôi đã thực hiện gần như chính xác cách được đề xuất (và tương tự như những gì dgvid đề xuất), ngoại trừ một số thay đổi nhỏ và một số thiếu sót được sửa. Tôi đã chọn phương pháp này bởi vì nó gần nhất với những gì tôi đang tìm kiếm ở nơi đầu tiên và không yêu cầu sử dụng bất kỳ tập tin thực thi bên thứ ba và như vậy. Nó hoạt động tuyệt vời!

Đây là những gì mã của tôi đã kết thúc tìm kiếm như:

EDIT: Tôi quyết định chuyển chức năng này để lắp ráp khác để tôi có thể tái sử dụng nó trong nhiều file (tôi chỉ vượt qua trong Assembly.GetExecutingAssembly()).

Đây là phiên bản cập nhật cho phép bạn chuyển qua hội thảo với các dll được nhúng.

embeddedResourcePrefix là đường dẫn chuỗi đến tài nguyên được nhúng, nó thường sẽ là tên của hội đồng theo sau là bất kỳ cấu trúc thư mục nào chứa tài nguyên (ví dụ: "MyComapny.MyProduct.MyAssembly.Resources" nếu dll nằm trong một thư mục có tên Tài nguyên trong dự án). Nó cũng giả định rằng dll có phần mở rộng .dll.resource.

public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) { 
     AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add => 
      try { 
       string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource"; 
       using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) { 
        return input != null 
         ? Assembly.Load(StreamToBytes(input)) 
         : null; 
       } 
      } catch (Exception ex) { 
       _log.Error("Error dynamically loading dll: " + args.Name, ex); 
       return null; 
      } 
     }; // Had to add colon 
    } 

    private static byte[] StreamToBytes(Stream input) { 
     int capacity = input.CanSeek ? (int)input.Length : 0; 
     using (MemoryStream output = new MemoryStream(capacity)) { 
      int readLength; 
      byte[] buffer = new byte[4096]; 

      do { 
       readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length 
       output.Write(buffer, 0, readLength); 
      } 
      while (readLength != 0); 

      return output.ToArray(); 
     } 
    } 
+0

Cảm ơn bạn đã đăng mã cuối cùng của mình, tôi có thể sẽ sử dụng nó ở đâu đó! –

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