67

\TemplatesDLL と EXE の隣のフォルダーにテンプレートを格納するプロジェクトがあります。

実行時にこのファイル パスを決定したいのですが、単体テスト内でも運用環境でも機能する手法を使用します (NUnit でシャドウ コピーを無効にしたくありません!)。

Assembly.LocationNUnit で実行すると、シャドウ コピーされたアセンブリのパスが返されるため、適切ではありません。

Environment.CommandLineNUnitなどでは、プロジェクトではなくNUnitへのパスを返すため、使用も制限されています。

Assembly.CodeBase有望に見えますが、これは UNC パスです:

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

これで、文字列操作を使用してこれをローカル ファイル システム パスに変換できましたが、.NET フレームワークのどこかに埋もれているよりクリーンな方法があるのではないかと思います。これを行うための推奨される方法を知っている人はいますか?

(このコンテキストでは、UNC パスがfile:///URL でない場合に例外をスローしてもまったく問題ありません)

4

5 に答える 5

119

System.Uri.LocalPath を使用する必要があります。

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

したがって、現在実行中のアセンブリの元の場所が必要な場合は、次のようにします。

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

LocalPath には、アセンブリのファイル名が含まれます。

D:\projects\MyApp\MyApp\bin\debug\MyApp.exe

アセンブリのディレクトリが必要な場合は、System.IO.Path.GetDirectoryName() を使用します。

string localDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);

それはあなたに与えるでしょう:

D:\projects\MyApp\MyApp\bin\debug
于 2010-11-05T16:12:12.047 に答える
29

Assembly.CodeBase は有望に見えますが、UNC パスです。

UNC パスではなくファイルの uriに近いものであることに注意してください。


これを解決するには、文字列を手動で操作します。真剣に。

SO で見つけられる他のすべての方法を、次のディレクトリ (逐語的に) で試してください。

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

これは有効な Windows パスです。(パスにこれらの文字のいずれかが含まれている人もいますが、それらすべてに対して機能する方法が必要ですよね?)

利用可能なコード ベース (必要ありませんLocationよね? ) のプロパティは次のとおりです (.NET 4 を使用する私の Win7 では):

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

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

次の点に注意してください。

  • CodeBaseはまったくエスケープされません。通常のローカル パスにプレフィックスが付けfile:///られ、バックスラッシュが置き換えられているだけです。そのため、これを にフィードしても機能しませんSystem.Uri
  • EscapedCodeBaseは完全にエスケープされていません (これがバグなのか、URI スキームの欠点なのかはわかりませ):
    • スペース文字 ( ) がどのように変換されるかに注意してください。%20
    • しかし、%20シーケンスは!に変換されます。%20(パーセント%はまったくエスケープされません)
    • この無残な形から元の姿を再現することは誰にもできません!

CodeBaseローカルファイルの場合 (そして、ファイルがローカルでない場合は、おそらく.Locationとにかく使用したいので、それが本当に私が気にかけていることのすべてです。

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

より良い解決策を思いつくことができると確信しています。おそらく、CodeBaseそれがローカルパスである場合、プロパティの適切な URL エンコード/デコードを行うソリューションを思いつくことさえできますが、単に削除して実行できることfile:///を考えるとそれは、確かに本当に醜いとしても、このソリューションは十分に優れていると思います。

于 2015-02-04T10:45:12.723 に答える
5

これはうまくいくはずです:

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

これを使用して、スタンドアロンの log4net.config ファイルを使用して dll ライブラリ内からログを記録できるようにしています。

于 2013-06-12T08:08:54.760 に答える
4

複雑なパスを含むもう 1 つのソリューション:

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

およびテスト:

    [Test]
    public void GetPathFromUriTest()
    {
        Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release", @"file:///C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release".GetPathFromUri());
        Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release",  @"file://C:/Test/Space( )(h#)(p%20){[a&],t@,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));
    }
于 2016-12-17T21:30:59.053 に答える
2

この質問にNUnitAssemblyHelper.GetDirectoryNameというタグを付けたので、実行中のアセンブリの元のディレクトリを取得するために使用することもできます。

using System.Reflection;
using NUnit.Framework.Internal;
...
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())
于 2016-03-12T12:54:37.320 に答える