web-dev-qa-db-ja.com

Assembly.CodeBaseをC#のファイルシステムパスに変換するにはどうすればよいですか?

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

実行時にこのファイルパスを特定したいのですが、ユニットテスト内でも本番環境でも機能するテクニックを使用しています(NUnitでシャドウコピーを無効にしたくない!)

Assembly.Locationは、NUnitの下で実行すると、シャドウコピーされたアセンブリのパスを返すため、問題があります。

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

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

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

今私はcouldを文字列操作を使用してローカルのファイルシステムパスに変換しますが、.NETフレームワークのどこかに埋め込むよりわかりやすい方法があると思います。誰かがこれを行う推奨方法を知っていますか?

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

63
Dylan Beattie

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;
105
Polyfun

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

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


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

あなたが見つけることができる他のすべての方法を試してください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は完全にエスケープされていません(私はnotこれがバグなのか、それとも RIスキーム の欠点なのかを知っています) :
    • スペース文字()が%20にどのように変換されるかに注意してください
    • しかし、%20シーケンスまた、%20に変換されます! (パーセント%はまったくエスケープされません)
    • 誰も、この壊れた形からオリジナルを再構築することはできません!

ローカルファイルの場合(CodeBasestuffについて私が気にかけているのはこれだけです。ファイルがローカルでない場合は、とにかく.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:///をオフにして、それで完了したら、確かに本当に醜い場合、このソリューションは十分に優れていると思います。

23
Martin Ba

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

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ライブラリ内からログを記録できるようにしています。

5
mmmmmm

複雑なパスを含むもう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));
    }
3
ili

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

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