2010-11-05 29 views
43

Tôi có một dự án lưu trữ các mẫu trong một thư mục \Templates bên cạnh các tệp DLL và EXE.Làm thế nào tôi có thể chuyển đổi Assembly.CodeBase thành một đường dẫn hệ thống tập tin trong C#?

Tôi muốn xác định đường dẫn tập tin này trong thời gian chạy, nhưng sử dụng một kỹ thuật mà sẽ làm việc bên trong một thử nghiệm đơn vị cũng như trong sản xuất (và tôi không muốn vô hiệu hóa bóng-sao chép trong NUnit!)

Assembly.Location là không tốt vì nó trả về đường dẫn của cụm sao chép được sao chép khi chạy dưới NUnit.

Environment.CommandLine cũng bị hạn chế sử dụng vì trong NUnit và cộng sự, nó trả về đường dẫn đến NUnit, không phải cho dự án của tôi.

Assembly.CodeBase trông đầy hứa hẹn, nhưng đó là một đường dẫn UNC:

file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe 

Bây giờ tôi thể tắt chức năng này vào một con đường hệ thống tập tin địa phương sử dụng chuỗi thao tác, nhưng tôi nghi ngờ có một cách sạch hơn để làm việc đó chôn vùi trong .NET framework ở đâu đó. Bất cứ ai biết một cách đề nghị làm điều này?

(Ném một ngoại lệ nếu đường dẫn UNC không phải là một URL file:/// là hoàn toàn tốt trong bối cảnh này)

+0

dupl có thể có icate của [Có phương pháp .NET Framework để chuyển đổi các tệp URI thành các đường dẫn có ký tự ổ đĩa không?] (http://stackoverflow.com/questions/278761/is-there-a-net-framework-method-for-converting- file-uris-to-path-với-drive-le) – nawfal

Trả lời

87

Bạn cần phải sử dụng System.Uri.LocalPath:

string localPath = new Uri("file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe").LocalPath; 

Vì vậy, nếu bạn muốn vị trí ban đầu của lắp ráp hiện đang thực hiện:

string localPath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath; 
+0

Đẹp. Cảm ơn nhiều. –

+0

Awesomeballs! Cảm ơn bạn, cảm ơn bạn, cảm ơn bạn! –

+4

Lưu ý rằng điều này không hoạt động khi một thư mục có '#' trong tên của nó – drake7707

5

này nên làm việc:

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); 
Assembly asm = Assembly.GetCallingAssembly(); 
String path = Path.GetDirectoryName(new Uri(asm.EscapedCodeBase).LocalPath); 

string strLog4NetConfigPath = System.IO.Path.Combine(path, "log4net.config"); 

Tôi đang sử dụng tính năng này để có thể đăng nhập từ bên trong thư viện dll bằng tệp log4net.config độc lập.

17

Assembly.CodeBase trông đầy hứa hẹn, nhưng đó là một đường dẫn UNC:

Do lưu ý rằng nó là một cái gì đó gần giống một file uri, không một UNC path.


Bạn giải quyết điều này bằng cách thực hiện thao tác chuỗi bằng tay. Nghiêm túc.

Hãy thử tất cả các phương pháp khác mà bạn có thể tìm thấy trên SO với các thư mục sau (nguyên văn):

C:\Test\Space()(h#)(p%20){[a&],[email protected],p%,+}.,\Release 

Đây là một, nếu hơi bất thường, Windows đường dẫn hợp lệ. (Một số người sẽ có hoặc là một trong những nhân vật trong đó đường đi, và bạn sẽ muốn bạn phương pháp để làm việc cho tất cả của những người, phải không?)

Các cơ sở mã có sẵn (we do not want Location, right?) tài sản này sau đó (trên Win7 của tôi với.NET 4):

assembly.CodeBase -> file:///C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release 

assembly.EscapedCodeBase -> file:///C:/Test/Space(%20)(h%23)(p%20)%7B%5Ba%26%5D,[email protected],p%,+%7D.,/Release 

Bạn sẽ lưu ý:

  • CodeBase không được thoát nào cả, nó chỉ là con đường địa phương thường xuyên bắt đầu bằng file:/// và backslashes thay thế. Như vậy, số không hoạt động để cấp dữ liệu này cho System.Uri.
  • EscapedCodeBase không được thoát hoàn toàn (Tôi làm không biết nếu điều này là một lỗi hoặc nếu đây là một thiếu sót của URI scheme):
    • Lưu ý cách nhân vật không gian () dịch để %20
    • nhưng trình tự %20 cũng là cũng chuyển thành %20! (phần trăm % không được thoát hoàn toàn)
    • Không ai có thể tạo lại bản gốc từ biểu mẫu bị xáo trộn này!

Đối với các file cục bộ (Và đó là thực sự tất cả tôi muốn quan tâm cho những thứ CodeBase, bởi vì nếu các tập tin không phải là địa phương, bạn có thể muốn sử dụng .Location dù sao, các công trình sau đây cho tôi (lưu ý rằng nó không phải là đẹp nhất hoặc là:

public static string GetAssemblyFullPath(Assembly assembly) 
    { 
     string codeBasePseudoUrl = assembly.CodeBase; // "pseudo" because it is not properly escaped 
     if (codeBasePseudoUrl != null) { 
      const string filePrefix3 = @"file:///"; 
      if (codeBasePseudoUrl.StartsWith(filePrefix3)) { 
       string sPath = codeBasePseudoUrl.Substring(filePrefix3.Length); 
       string bsPath = sPath.Replace('/', '\\'); 
       Console.WriteLine("bsPath: " + bsPath); 
       string fp = Path.GetFullPath(bsPath); 
       Console.WriteLine("fp: " + fp); 
       return fp; 
      } 
     } 
     System.Diagnostics.Debug.Assert(false, "CodeBase evaluation failed! - Using Location as fallback."); 
     return Path.GetFullPath(assembly.Location); 
    } 

tôi chắc chắn người ta có thể đưa ra giải pháp tốt hơn, có lẽ là một thậm chí có thể đưa ra một giải pháp mà không thích URL en-/giải mã của CodeBase tài sản nếu đó là một con đường địa phương, nhưng cho rằng một trong những chỉ có thể tách ra khỏi file:/// một d được thực hiện với nó, tôi muốn nói giải pháp này là viết tắt là đủ tốt, nếu chắc chắn thực sự xấu xí.

+1

Nếu đó là đường dẫn UNC, đó là tệp: //server/share/path/to/assembly.dll (chỉ có 2 dấu gạch chéo). – Joshua

+0

@Joshua - bạn muốn nói rằng đường dẫn '\\ server \ ...' được mã hóa chỉ với hai dấu gạch chéo, thay vì 3 khi có ổ đĩa cục bộ? –

+0

@Joshua - bạn có biết cách đường dẫn '\\? \ UNC \ server \ share' được mã hóa không? –

1

Vì bạn gắn thẻ câu hỏi này NUnit, bạn cũng có thể sử dụng AssemblyHelper.GetDirectoryName để có được thư mục gốc của các hội đồng thi:

using System.Reflection; 
using NUnit.Framework.Internal; 
... 
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly()) 
2

hơn Một giải pháp, trong đó có con đường phức tạp:

public static string GetPath(this Assembly assembly) 
    { 
     return Path.GetDirectoryName(assembly.GetFileName()); 
    } 

    public static string GetFileName(this Assembly assembly) 
    { 
     return assembly.CodeBase.GetPathFromUri(); 
    } 

    public static string GetPathFromUri(this string uriString) 
    { 
     var uri = new Uri(Uri.EscapeUriString(uriString)); 
     return String.Format("{0}{1}", Uri.UnescapeDataString(uri.PathAndQuery), Uri.UnescapeDataString(uri.Fragment)); 
    } 

và thử nghiệm:

[Test] 
    public void GetPathFromUriTest() 
    { 
     Assert.AreEqual(@"C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release", @"file:///C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release".GetPathFromUri()); 
     Assert.AreEqual(@"C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release", @"file://C:/Test/Space()(h#)(p%20){[a&],[email protected],p%,+}.,/Release".GetPathFromUri()); 
    } 

    [Test] 
    public void AssemblyPathTest() 
    { 
     var asm = Assembly.GetExecutingAssembly(); 

     var path = asm.GetPath(); 
     var file = asm.GetFileName(); 

     Assert.IsNotEmpty(path); 
     Assert.IsNotEmpty(file); 

     Assert.That(File  .Exists(file)); 
     Assert.That(Directory.Exists(path)); 
    } 
+0

Rất tuyệt! Nhưng tôi thích các dấu gạch chéo ngược, vì vậy tôi sẽ chuyển kết quả từ 'GetPathFromUri' thông qua' Path.GetFullPath'. – tm1

+0

Bạn sẽ cần phải đưa Uri.Host vào tài khoản để hỗ trợ đường dẫn mạng. – tm1

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