【问题标题】:.NET Settings Relative Path.NET 设置相对路径
【发布时间】:2023-10-29 11:26:01
【问题描述】:

我正在开发一个应用程序,其中我有一个相对于我的应用程序根目录的图像文件夹。我希望能够在 Properties -> Settings 设计器中指定这个相对路径,例如。 “\图片\”。我遇到的问题是在 Environment.CurrentDirectory 通过 OpenFileDialog 更改的情况下,相对路径无法解析到正确的位置。有没有办法在设置文件中指定一个路径,该路径意味着始终从应用程序目录而不是当前目录开始?我知道我总是可以将应用程序路径动态连接到相对路径的前面,但我希望我的 Settings 属性能够自行解析。

【问题讨论】:

    标签: visual-studio configuration properties settings


    【解决方案1】:

    我使用以下两种方法来帮助解决这个问题:

    public static IEnumerable<DirectoryInfo> ParentDirs(this DirectoryInfo dir) {
        while (dir != null) {
            yield return dir;
            dir = dir.Parent;
        }
    }
    public static DirectoryInfo FindDataDir(string relpath, Assembly assembly) {
        return new FileInfo((assembly).Location)
            .Directory.ParentDirs()
            .Select(dir => Path.Combine(dir.FullName + @"\", relpath))
            .Where(Directory.Exists)
            .Select(path => new DirectoryInfo(path))
            .FirstOrDefault();
    }
    

    当各种构建脚本最终将东西粘在bin\x64\Release\NonsensePath\等目录中时,查看父目录以便在开发过程中更容易使用的原因。

    【讨论】:

      【解决方案2】:

      I suggest you使用Assembly.CodeBase,如下图:

      public static string RealAssemblyFilePath()
      {
         string dllPath=Assembly.GetExecutingAssembly().CodeBase.Substring(8);
         return dllPath;
      }
      

      你可以试试Application.ExecutablePath。但是您需要参考 System.Windows.Forms。如果您希望您的类库避开表单和 UI 内容,这可能不是一个好主意。

      你可以试试Assembly.GetExecutingAssembly().Location。但是,如果您在运行应用程序之前以某种方式执行“影子复制”(如默认的 NUnit 行为),那么此属性将返回影子复制位置,而不是真实的物理位置。

      最好的方法是实现一个调用Assembly 对象的CodeBase 属性的函数,并去掉字符串中不相关的部分。

      【讨论】:

        【解决方案3】:

        这似乎在 WinForms 和 ASP.NET 中都有效(提供 配置文件 的路径):

        new System.IO.FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile).Directory;
        

        对于 Windows 和控制台应用程序,显而易见的方法是使用:

        Application.StartupPath
        

        【讨论】:

          【解决方案4】:

          您在寻找 Application.ExecutablePath 吗?这应该会告诉您应用程序的可执行文件在哪里,删除可执行文件名称,然后将您的路径附加到它。

          【讨论】:

          • 只有当它是一个 Windows 窗体应用程序时才有效。它还返回启动应用程序的可执行文件的路径,包括可执行文件名,因此您只需去掉路径部分。
          • 由于提到了 Environment.CurrentDirectory,我假设这是一个 .NET 应用程序。
          • .NET 应用程序并不一定意味着它是 WinForms 应用程序。您可以编写命令行应用程序的 .NET 应用程序,这些应用程序无法访问 Application 类。
          • 这不会在 ASP.NET 中也崩溃吗?
          • 克里斯,是的,我相信出于同样的原因。 Application 类是 System.Windows.Forms 程序集的一部分,它也不适用于 ASP.NET 应用程序。
          【解决方案5】:

          2 个选项:

          • 使用该设置的代码可以针对当前执行程序集的目录解析该设置。
          • 您可以创建自己的类型,将其序列化为相对于正在执行的程序集的字符串,并具有完整路径的访问器,该访问器将针对当前正在执行的程序集的目录进行解析。

          代码示例:

          string absolutePath = Settings.Default.ImagePath;
          if(!Path.IsPathRooted(absolutePath))
          {
              string root = Assembly.GetEntryAssembly().Location;
              root = Path.GetDirectoryName(root);
              absolutePath = Path.Combine(root, absolutePath);
          }
          

          这段代码的好处是它允许在您的设置中使用完全限定路径或相对路径。如果您需要相对于不同程序集的路径,您可以更改您使用的程序集的位置 - GetExecutingAssembly() 将为您提供程序集的位置以及您正在运行的代码,如果您使用 GetCallingAssembly() 会很好选择选项 2。

          【讨论】:

            【解决方案6】:

            据我所知,没有允许这种类型的路径解析的内置功能。您最好的选择是动态确定应用程序执行目录并将您的图像路径连接到它。由于您提到的原因,您不想使用 Environment.CurrentDirectory - 当前目录对于这种情况可能并不总是正确的。

            我发现找到执行程序集位置的最安全代码是这样的:

            public string ExecutingAssemblyPath()
            {
               Assembly actualAssembly = Assembly.GetEntryAssembly();
               if (this.actualAssembly == null)
               {
                  actualAssembly = Assembly.GetCallingAssembly();
               }
               return actualAssembly.Location;
            }
            

            【讨论】:

            • 您有一个看起来像错误的this. - 在什么情况下您实际上需要那个if 声明?顺便说一句,请注意,如果概念调用者被运行时内联,GetCallingAssembly 可能会做一些令人惊讶的事情。出于这个原因,我避免使用这种方法,而是简单地通过Type 查找程序集。这至少是安全的。