2009-09-03 41 views
108

DLL của tôi được tải bởi ứng dụng của bên thứ ba mà chúng tôi không thể tùy chỉnh. Hội đồng của tôi phải được đặt trong thư mục riêng của họ. Tôi không thể đưa chúng vào GAC (ứng dụng của tôi có yêu cầu được triển khai bằng XCOPY). Khi DLL gốc cố tải tài nguyên hoặc loại từ một DLL khác (trong cùng một thư mục), tải không thành công (FileNotFound). Có thể thêm thư mục chứa các tệp DLL của tôi vào đường dẫn tìm kiếm lắp ráp theo chương trình (từ gốc DLL) không? Tôi không được phép thay đổi các tập tin cấu hình của ứng dụng.Cách thêm thư mục vào đường dẫn tìm kiếm lắp ráp khi chạy trong .NET?

Trả lời

45

Bạn có thể thêm probing path vào tệp .config của ứng dụng, nhưng nó sẽ chỉ hoạt động nếu đường dẫn thăm dò nằm trong thư mục cơ sở của ứng dụng của bạn.

+3

Cảm ơn bạn đã thêm điều này. Tôi đã nhìn thấy các giải pháp 'AssemblyResolve' rất nhiều lần, tốt để có một tùy chọn khác (và dễ dàng hơn). –

130

Có vẻ như bạn có thể sử dụng sự kiện AppDomain.AssemblyResolve và tải thủ công các phụ thuộc từ thư mục DLL của bạn.

Chỉnh sửa (từ bình luận):

AppDomain currentDomain = AppDomain.CurrentDomain; 
currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder); 

static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args) 
{ 
    string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
    string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll"); 
    if (!File.Exists(assemblyPath)) return null; 
    Assembly assembly = Assembly.LoadFrom(assemblyPath); 
    return assembly; 
} 
+2

Cảm ơn bạn, Mattias! Công trình này: AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve + = new ResolveEventHandler (LoadFromSameFolderResolveEventHandler); Tải lắp ráp tĩnhFromSameFolderResolveEventHandler (đối tượng người gửi, ResolveEventArgs args) { thư mục chuỗiPath = Đường dẫn.GetDirectoryName (Assembly.GetExecutingAssembly(). Vị trí); chuỗi assemblyPath = Path.Combine (folderPath, args.Name + ".dll"); Assembly assembly = Assembly.LoadFrom (assemblyPath); lắp ráp trả lại; } – isobretatel

+0

Giải pháp tuyệt vời! Dưới đây là một bài viết mô tả một vài giải pháp khác bao gồm một trong những chi tiết này: http://kbalertz.com/897297/consume-assemblies-located-folder-different-application-folder-Visual-Basic.aspx (cảnh báo: nó cho VB , không phải C# và một chút cũ) – Domenic

+0

Bạn sẽ làm gì nếu bạn muốn "dự phòng" cho Trình giải quyết cơ bản. ví dụ. 'if (! File.Exists (asmPath)) trả về tìm kiếmInGAC (...);' –

3

nhìn vào AppDomain.AppendPrivatePath (phản đối) hoặc AppDomainSetup.PrivateBinPath

+10

Từ [MSDN] (http://msdn.microsoft.com/en-us/library/system.appdomainsetup.aspx): Thay đổi các thuộc tính của một cá thể AppDomainSetup không ảnh hưởng đến bất kỳ AppDomain hiện có nào. Nó chỉ có thể ảnh hưởng đến việc tạo một AppDomain mới, khi phương thức CreateDomain được gọi với cá thể AppDomainSetup như một tham số. – Nathan

+1

['AppDomain.AppendPrivatePath'] (https://msdn.microsoft.com/en-us/library/system.appdomain.appendprivatepath%28v=vs.110%29.aspx) 's tài liệu hướng dẫn dường như gợi ý rằng nó nên hỗ trợ tự động mở rộng đường dẫn tìm kiếm của 'AppDomain', chỉ là tính năng không còn được dùng nữa. Nếu nó hoạt động, nó là một giải pháp sạch hơn nhiều so với quá tải 'AssemblyResolve'. – binki

8

Lời giải thích tốt nhất from MS itself:

AppDomain currentDomain = AppDomain.CurrentDomain; 
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler); 

private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) 
{ 
    //This handler is called only when the common language runtime tries to bind to the assembly and fails. 

    //Retrieve the list of referenced assemblies in an array of AssemblyName. 
    Assembly MyAssembly, objExecutingAssembly; 
    string strTempAssmbPath = ""; 

    objExecutingAssembly = Assembly.GetExecutingAssembly(); 
    AssemblyName[] arrReferencedAssmbNames = objExecutingAssembly.GetReferencedAssemblies(); 

    //Loop through the array of referenced assembly names. 
    foreach(AssemblyName strAssmbName in arrReferencedAssmbNames) 
    { 
     //Check for the assembly names that have raised the "AssemblyResolve" event. 
     if(strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(","))) 
     { 
      //Build the path of the assembly from where it has to be loaded.     
      strTempAssmbPath = "C:\\Myassemblies\\" + args.Name.Substring(0,args.Name.IndexOf(","))+".dll"; 
      break; 
     } 

    } 

    //Load the assembly from the specified path.      
    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);     

    //Return the loaded assembly. 
    return MyAssembly;   
} 
+0

'AssemblyResolve' là cho CurrentDomain, không hợp lệ cho một tên miền khác' AppDomain.CreateDomain' – Kiquenet

11

Cập nhật cho Framework 4

Kể từ khi Khung 4 nâng cao Assembl yResolve sự kiện cũng cho các tài nguyên thực sự xử lý này hoạt động tốt hơn. Nó dựa trên khái niệm rằng bản địa hóa nằm trong thư mục con ứng dụng (một cho bản địa hóa với tên của nền văn hóa nghĩa là C: \ MyApp \ it cho tiếng Ý) Bên trong có tệp tài nguyên. Trình xử lý cũng hoạt động nếu nội địa hóa là quốc gia, tức là it-IT hoặc pt-BR. Trong trường hợp này, trình xử lý "có thể được gọi nhiều lần: một lần cho mỗi văn hóa trong chuỗi dự phòng" [từ MSDN]. Điều này có nghĩa là nếu chúng ta trả về null cho tệp tài nguyên "it-IT", khung công tác sẽ làm tăng sự kiện yêu cầu "nó".

móc Event

 AppDomain currentDomain = AppDomain.CurrentDomain; 
     currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve); 

Event handler

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     //This handler is called only when the common language runtime tries to bind to the assembly and fails. 

     Assembly executingAssembly = Assembly.GetExecutingAssembly(); 

     string applicationDirectory = Path.GetDirectoryName(executingAssembly.Location); 

     string[] fields = args.Name.Split(','); 
     string assemblyName = fields[0]; 
     string assemblyCulture; 
     if (fields.Length < 2) 
      assemblyCulture = null; 
     else 
      assemblyCulture = fields[2].Substring(fields[2].IndexOf('=') + 1); 


     string assemblyFileName = assemblyName + ".dll"; 
     string assemblyPath; 

     if (assemblyName.EndsWith(".resources")) 
     { 
      // Specific resources are located in app subdirectories 
      string resourceDirectory = Path.Combine(applicationDirectory, assemblyCulture); 

      assemblyPath = Path.Combine(resourceDirectory, assemblyFileName); 
     } 
     else 
     { 
      assemblyPath = Path.Combine(applicationDirectory, assemblyFileName); 
     } 



     if (File.Exists(assemblyPath)) 
     { 
      //Load the assembly from the specified path.      
      Assembly loadingAssembly = Assembly.LoadFrom(assemblyPath); 

      //Return the loaded assembly. 
      return loadingAssembly; 
     } 
     else 
     { 
      return null; 
     } 

    } 
6

Đối với người dùng C++/CLI, đây là câu trả lời @Mattias S'(mà làm việc cho tôi):

using namespace System; 
using namespace System::IO; 
using namespace System::Reflection; 

static Assembly ^LoadFromSameFolder(Object ^sender, ResolveEventArgs ^args) 
{ 
    String ^folderPath = Path::GetDirectoryName(Assembly::GetExecutingAssembly()->Location); 
    String ^assemblyPath = Path::Combine(folderPath, (gcnew AssemblyName(args->Name))->Name + ".dll"); 
    if (File::Exists(assemblyPath) == false) return nullptr; 
    Assembly ^assembly = Assembly::LoadFrom(assemblyPath); 
    return assembly; 
} 

// put this somewhere you know it will run (early, when the DLL gets loaded) 
System::AppDomain ^currentDomain = AppDomain::CurrentDomain; 
currentDomain->AssemblyResolve += gcnew ResolveEventHandler(LoadFromSameFolder); 
0

tôi đã sử dụng giải pháp @Mattias S '. Nếu bạn thực sự muốn giải quyết các phụ thuộc từ cùng một thư mục - bạn nên thử sử dụng Yêu cầu lắp ráp vị trí, như được hiển thị bên dưới. args.RequestingAssembly cần được kiểm tra về tính rỗng.

System.AppDomain.CurrentDomain.AssemblyResolve += (s, args) => 
{ 
    var loadedAssembly = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == args.Name).FirstOrDefault(); 
    if(loadedAssembly != null) 
    { 
     return loadedAssembly; 
    } 

    if (args.RequestingAssembly == null) return null; 

    string folderPath = Path.GetDirectoryName(args.RequestingAssembly.Location); 
    string rawAssemblyPath = Path.Combine(folderPath, new System.Reflection.AssemblyName(args.Name).Name); 

    string assemblyPath = rawAssemblyPath + ".dll"; 

    if (!File.Exists(assemblyPath)) 
    { 
     assemblyPath = rawAssemblyPath + ".exe"; 
     if (!File.Exists(assemblyPath)) return null; 
    } 

    var assembly = System.Reflection.Assembly.LoadFrom(assemblyPath); 
    return assembly; 
}; 
Các vấn đề liên quan