【问题标题】:How can I get the application's path in a .NET console application?如何在 .NET 控制台应用程序中获取应用程序的路径?
【发布时间】:2010-10-24 15:53:17
【问题描述】:

如何在控制台应用程序中找到应用程序的路径?

Windows Forms 中,我可以使用Application.StartupPath 查找当前路径,但这似乎在控制台应用程序中不可用。

【问题讨论】:

  • 您是否在目标(客户端、开发)机器上安装 .NET Framework?如果你的回答是真的;因此,您可以添加对 System.Windows.Forms.dll 的引用并使用 Application.StartupPath!如果您想丢弃更多的未来异常,这是最好的方法!
  • AppDomain.BaseDirectory 是应用程序目录。请注意,应用程序在 VS 环境和 Win 环境中的行为可能不同。但 AppDomain 不应该与 application.path 相同,但我希望这不仅适用于 IIS。

标签: c# .net console console-application


【解决方案1】:

System.Reflection.Assembly.GetExecutingAssembly().Location1

如果您想要的只是目录,请将其与 System.IO.Path.GetDirectoryName 组合。

1根据 Mr.Mindor 的评论:
System.Reflection.Assembly.GetExecutingAssembly().Location 返回执行程序集当前所在的位置,可能是也可能不是程序集所在的位置不执行时定位。在卷影复制程序集的情况下,您将获得临时目录中的路径。 System.Reflection.Assembly.GetExecutingAssembly().CodeBase 将返回程序集的“永久”路径。

【讨论】:

  • System.Reflection.Assembly.GetExecutingAssembly().Location 返回正在执行的程序集当前所在的位置,该位置可能是也可能不是程序集未执行时所在的位置。在卷影复制程序集的情况下,您将获得临时目录中的路径。 System.Reflection.Assembly.GetExecutingAssembly().CodeBase 将返回程序集的“永久”路径。
  • @SamGoldberg:这取决于它的使用方式:stackoverflow.com/q/1068420/391656。或者你可以... new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath
  • GetExecutingAssembly 返回包含当前正在执行的代码的程序集。这不一定是控制台 .exe 程序集。它可能是从完全不同的位置加载的程序集。您将不得不使用GetEntryAssembly!另请注意,当程序集位于 GAC 中时,可能不会设置 CodeBase。更好的选择是AppDomain.CurrentDomain.BaseDirectory
  • 请在4个空格内写代码,以便于复制
  • @farosch: Application 不存在控制台应用程序。
【解决方案2】:

您可以使用以下代码获取当前应用程序目录。

AppDomain.CurrentDomain.BaseDirectory

【讨论】:

  • 不要使用这个。 BaseDirectory 可以在运行时设置。它不能保证是正确的(就像接受的答案一样)。
  • +1 这可能是您想要的答案,因为它可以补偿卷影复制。
  • @usr 是什么让您认为BaseDirectory 可以在运行时设置?它只有一个吸气剂。
  • @bitbonk 可以在 appdomain 创建时设置。
  • BaseDirectory 不是可以在 *.lnk 文件中的“Start in:”字段中更改吗?
【解决方案3】:

您有两个选项来查找应用程序的目录,您的选择取决于您的目的。

// to get the location the assembly is executing from
//(not necessarily where the it normally resides on disk)
// in the case of the using shadow copies, for instance in NUnit tests, 
// this will be in a temp directory.
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;

//To get the location the assembly normally resides on disk or the install directory
string path = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;

//once you have the path you get the directory with:
var directory = System.IO.Path.GetDirectoryName(path);

【讨论】:

  • 只是想说,显然有很多超过2个选项被发布了多少其他选项......
  • 如果您要对所述路径执行的任何操作都不支持 URI 格式,请使用 var localDirectory = new Uri(directory).LocalPath;
  • 这是错误的。什么是可执行文件根本不是 .NET 程序集?正确的答案是检查环境并检查命令行。
  • @Ukuma.Scott 如果路径包含 & 或 #,这将不起作用
【解决方案4】:

可能有点晚了,但值得一提:

Environment.GetCommandLineArgs()[0];

或者更准确地说是获取目录路径:

System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);

编辑:

不少人指出GetCommandLineArgs不保证返回程序名。见The first word on the command line is the program name only by convention。该文章确实指出“尽管极少数 Windows 程序使用此怪癖(我自己不知道)”。因此可以“欺骗”GetCommandLineArgs,但我们谈论的是控制台应用程序。控制台应用程序通常又快又脏。所以这符合我的 KISS 理念。

【讨论】:

  • @usr 你提到的情况是高度理论化的。在控制台应用程序的上下文中,使用任何其他方法都没有任何意义。保持简单!
  • @usr mmm - 查看 taskmgr cmdline 列有点支持我所说的。一些只有 exe 名称的系统服务。没关系。我想说的是,在开发控制台应用程序时,没有必要让事情变得比他们需要的更复杂。尤其是当我们已经有了可用的信息时。现在,如果您以欺骗 GetCommandLineArgs 的方式运行控制台应用程序,那么您已经跳过了障碍,您可能需要问自己控制台应用程序是否是正确的方法。
  • 您的“简单”解决方案涉及两个方法调用。 “复杂”的解决方案涉及两个方法调用。没有实际的区别——除了“简单”的解决方案可能会在某些情况下给你错误的答案,而这些情况在你编写程序时是不受你控制的。为什么要冒险?使用另外两个方法调用,你的程序不会更复杂,但会更可靠。
  • 适用于我的场景,其他解决方案没有,所以感谢您提供另一种选择 :-) 我正在使用 ReSharper 测试运行程序来运行 MS 单元测试,而我正在测试的代码需要特定的 . dll 在执行目录中......并且 Assembly.GetExecutingDirectory() 奇怪地返回不同的结果。
  • @Chris - 为这个答案辩护。它适用于单元测试,GetEntryAssembly 解决方案不适用,因为 GetEntryAssembly 返回 null。建议 GetExecutingAssembly 的答案是虚假的,因为它们仅在执行程序集是可执行文件时才返回可执行文件。这不是简单的,而是正确的解决方案。
【解决方案5】:

适用于对 asp.net 网络应用程序感兴趣的任何人。这是我的 3 种不同方法的结果

protected void Application_Start(object sender, EventArgs e)
{
  string p1 = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  string p2 = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
  string p3 = this.Server.MapPath("");
  Console.WriteLine("p1 = " + p1);
  Console.WriteLine("p2 = " + p2);
  Console.WriteLine("p3 = " + p3);
}

结果

p1 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\a897dd66\ec73ff95\assembly\dl3\ff65202d\29daade3_5e84cc01
p2 = C:\inetpub\SBSPortal_staging\
p3 = C:\inetpub\SBSPortal_staging

应用程序从“C:\inetpub\SBSPortal_staging”物理运行,因此第一个解决方案绝对不适合网络应用程序。

【讨论】:

    【解决方案6】:

    上面的答案是我需要的 90%,但为我返回了 Uri 而不是常规路径。

    正如 MSDN 论坛帖子 How to convert URI path to normal filepath? 中所述,我使用了以下内容:

    // Get normal filepath of this assembly's permanent directory
    var path = new Uri(
        System.IO.Path.GetDirectoryName(
            System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
        ).LocalPath;
    

    【讨论】:

    • 如果有问题的 exe 是 Windows 服务并且当前目录返回 C:\Windows\system32,这也很有效。以上代码返回exe的实际位置
    • 除非您随后尝试执行File.CreateDirectory(path) 之类的操作,否则它会给您一个例外,即它不允许 URI 路径...
    • 不幸的是,这不适用于包含片段标识符(# 字符)的路径。标识符及其后面的所有内容都会从生成的路径中截断。
    • 你为什么不交换new UriSystem.IO.Path.GetDirectoryName?这会给你一个普通的路径字符串而不是Uri
    • 我觉得这是最好的。在任何环境中,同样的方法对我来说都是可靠的。在生产中,本地调试,单元测试......想要打开您在单元测试中包含的内容文件(“内容 - 如果较新则复制”)?它就在那里。
    【解决方案7】:

    您可能希望这样做:

    System.IO.Path.GetDirectoryName(
        System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
    

    【讨论】:

    • 请注意,这会带回一个 URI,但并非在所有情况下都支持。
    【解决方案8】:

    如果您正在寻找与 .NET Core 兼容的方式,请使用

    System.AppContext.BaseDirectory
    

    这是在 .NET Framework 4.6 和 .NET Core 1.0(和 .NET Standard 1.3)中引入的。请参阅:AppContext.BaseDirectory Property

    根据this page

    这是 .NET Core 中 AppDomain.CurrentDomain.BaseDirectory 的首选替代品

    【讨论】:

    【解决方案9】:

    你可以用这个代替。

    System.Environment.CurrentDirectory
    

    【讨论】:

    • 这会得到可执行文件的文件夹
    • 这可以通过多种方式进行更改(快捷方式设置等)...最好不要使用它。
    【解决方案10】:

    对于控制台应用程序,您可以试试这个:

    System.IO.Directory.GetCurrentDirectory();
    

    输出(在我的本地机器上):

    c:\users\xxxxxxx\documents\visual studio 2012\Projects\ImageHandler\GetDir\bin\Debug

    或者你也可以试试(最后多了一个反斜杠):

    AppDomain.CurrentDomain.BaseDirectory
    

    输出:

    c:\users\xxxxxxx\documents\visual studio 2012\Projects\ImageHandler\GetDir\bin\Debug\

    【讨论】:

    • "BaseDirectory可以在运行时设置,不保证正确"
    【解决方案11】:

    我已经使用了这段代码并得到了解决方案。

    AppDomain.CurrentDomain.BaseDirectory
    

    【讨论】:

      【解决方案12】:

      以下行将为您提供应用程序路径:

      var applicationPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)
      

      上述解决方案在以下情况下正常工作:

      • 简单的应用程序
      • 在 Assembly.GetEntryAssembly() 将返回 null 的另一个域中
      • DLL 作为字节数组从嵌入式资源加载,并作为 Assembly.Load(byteArrayOfEmbeddedDll) 加载到 AppDomain
      • 与 Mono 的 mkbundle 捆绑包(没有其他方法有效)

      【讨论】:

      • 在 linux 上的调试器下返回:/usr/share/dotnet
      【解决方案13】:

      您可以简单地将System.Windows.Forms 添加到您的项目引用中,然后照常使用System.Windows.Forms.Application.StartupPath

      因此,不需要更复杂的方法或使用反射。

      【讨论】:

      • 我用过那个,效果很好。但是有一次我在我的单元测试项目中使用了这种方法。当然,它失败了,因为它正在 C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 14.0\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\TESTWINDOW 中寻找我的文件
      • @ainasiart 那么我如何在单元测试时让它工作??
      【解决方案14】:

      我用过

      System.AppDomain.CurrentDomain.BaseDirectory
      

      当我想查找相对于应用程序文件夹的路径时。这适用于 ASP.Net 和 winform 应用程序。它也不需要对 System.Web 程序集的任何引用。

      【讨论】:

        【解决方案15】:

        我的意思是,为什么不使用 p/invoke 方法?

            using System;
            using System.IO;
            using System.Runtime.InteropServices;
            using System.Text;
            public class AppInfo
            {
                    [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)]
                    private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
                    private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
                    public static string StartupPath
                    {
                        get
                        {
                            StringBuilder stringBuilder = new StringBuilder(260);
                            GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity);
                            return Path.GetDirectoryName(stringBuilder.ToString());
                        }
                    }
            }
        

        您可以像 Application.StartupPath 一样使用它:

            Console.WriteLine("The path to this executable is: " + AppInfo.StartupPath + "\\" + System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe");
        

        【讨论】:

        • 既然有这么多的 .NET 来解决这个问题,为什么还要 p/invoke?
        • @user3596865 因为它需要对 Windows 的硬依赖,并且与 DNX 或 Mono 不兼容。未来的 Windows 版本可能会发生重大变化。再说一遍:为什么我们应该在这里使用 pinvoke?
        【解决方案16】:

        Assembly.GetEntryAssembly().LocationAssembly.GetExecutingAssembly().Location

        System.IO.Path.GetDirectoryName()结合使用,只获取目录。

        GetEntryAssembly()GetExecutingAssembly() 的路径可以不同,即使在大多数情况下目录相同。

        对于GetEntryAssembly(),您必须注意,如果入口模块是非托管的(即C++ 或VB6 可执行文件),这可能会返回null。在这些情况下,可以使用 Win32 API 中的GetModuleFileName

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
        

        【讨论】:

          【解决方案17】:

          如果应该通过双击来调用exe,我会使用它

          var thisPath = System.IO.Directory.GetCurrentDirectory();
          

          【讨论】:

          • 这是不正确的,因为您可以在结果中获得随机目录。
          • 该命令返回Environment.CurrentDirectory,运行时可能会更改为任意路径,因此不是可靠的解决方案。
          【解决方案18】:

          在 VB.net 中

          My.Application.Info.DirectoryPath
          

          为我工作(应用程序类型:类库)。不确定C#... 以字符串形式返回不带文件名的路径

          【讨论】:

            【解决方案19】:
            AppDomain.CurrentDomain.BaseDirectory
            

            将通过安装包引用第 3 方参考文件来解决问题。

            【讨论】:

            • 这个答案已经在 5 年前被提出,甚至不止一次。
            【解决方案20】:

            我没有看到有人将 .Net Core 反射提供的 LocalPath 转换为可用的 System.IO 路径,所以这是我的版本。

            public static string GetApplicationRoot()
            {
               var exePath = new Uri(System.Reflection.
               Assembly.GetExecutingAssembly().CodeBase).LocalPath;
            
               return new FileInfo(exePath).DirectoryName;
                   
            }
            

            这将返回完整的 C:\\xxx\\xxx 格式化路径到您的代码所在的位置。

            【讨论】:

            • 我不明白Uri(System.Reflection. Assembly.GetExecutingAssembly().CodeBase).LocalPathSystem.Reflection.Assembly.GetExecutingAssembly().CodeBase 有什么不同你能详细说明吗?
            【解决方案21】:

            试试这行简单的代码:

             string exePath = Path.GetDirectoryName( Application.ExecutablePath);
            

            【讨论】:

              【解决方案22】:

              使用 .NET Core 3 及更高版本,您将获得 .dll 而不是 .exe 文件。获取可以使用的.exe文件路径。

              var appExePath = Process.GetCurrentProcess().MainModule.FileName;
              

              【讨论】:

                【解决方案23】:

                这些方法在特殊情况下都不起作用,例如使用指向 exe 的符号链接,它们将返回链接的位置而不是实际的 exe。

                所以可以使用QueryFullProcessImageName 来解决这个问题:

                using System;
                using System.IO;
                using System.Runtime.InteropServices;
                using System.Text;
                using System.Diagnostics;
                
                internal static class NativeMethods
                {
                    [DllImport("kernel32.dll", SetLastError = true)]
                    internal static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags, [Out]StringBuilder lpExeName, ref int lpdwSize);
                
                    [DllImport("kernel32.dll", SetLastError = true)]
                    internal static extern IntPtr OpenProcess(
                        UInt32 dwDesiredAccess,
                        [MarshalAs(UnmanagedType.Bool)]
                        Boolean bInheritHandle,
                        Int32 dwProcessId
                    );
                }
                
                public static class utils
                {
                
                    private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
                    private const UInt32 PROCESS_VM_READ = 0x010;
                
                    public static string getfolder()
                    {
                        Int32 pid = Process.GetCurrentProcess().Id;
                        int capacity = 2000;
                        StringBuilder sb = new StringBuilder(capacity);
                        IntPtr proc;
                
                        if ((proc = NativeMethods.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid)) == IntPtr.Zero)
                            return "";
                
                        NativeMethods.QueryFullProcessImageName(proc, 0, sb, ref capacity);
                
                        string fullPath = sb.ToString(0, capacity);
                
                        return Path.GetDirectoryName(fullPath) + @"\";
                    }
                }
                

                【讨论】:

                  【解决方案24】:

                  Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) 在我尝试过的每一种情况下,它都是唯一对我有用的。

                  【讨论】:

                    【解决方案25】:

                    【讨论】:

                    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
                    • @ErikMcKelvey 他们提供了一个解决方案,并参考了说明这实际上是一个解决方案的文档。您将如何在不使用链接的情况下引用解决方案?
                    【解决方案26】:

                    我将它用于控制台 + 网络 6

                    Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
                    

                    【讨论】:

                      【解决方案27】:

                      这是一个可靠的解决方案,适用于 32bit64bit 应用程序。

                      添加这些引用:

                      使用 System.Diagnostics;

                      使用 System.Management;

                      将此方法添加到您的项目中:

                      public static string GetProcessPath(int processId)
                      {
                          string MethodResult = "";
                          try
                          {
                              string Query = "SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = " + processId;
                      
                              using (ManagementObjectSearcher mos = new ManagementObjectSearcher(Query))
                              {
                                  using (ManagementObjectCollection moc = mos.Get())
                                  {
                                      string ExecutablePath = (from mo in moc.Cast<ManagementObject>() select mo["ExecutablePath"]).First().ToString();
                      
                                      MethodResult = ExecutablePath;
                      
                                  }
                      
                              }
                      
                          }
                          catch //(Exception ex)
                          {
                              //ex.HandleException();
                          }
                          return MethodResult;
                      }
                      

                      现在像这样使用它:

                      int RootProcessId = Process.GetCurrentProcess().Id;
                      
                      GetProcessPath(RootProcessId);
                      

                      注意,如果你知道进程的id,那么这个方法会返回对应的ExecutePath。

                      额外的,对于那些感兴趣的人:

                      Process.GetProcesses() 
                      

                      ...将为您提供所有当前正在运行的进程的数组,并且...

                      Process.GetCurrentProcess()
                      

                      ...将为您提供当前流程以及他们的信息,例如Id 等以及有限的控制,例如杀戮等*

                      【讨论】:

                        【解决方案28】:

                        您可以使用解决方案资源管理器在项目中创建一个文件夹名称为资源,然后您可以在资源中粘贴一个文件。

                        private void Form1_Load(object sender, EventArgs e) {
                            string appName = Environment.CurrentDirectory;
                            int l = appName.Length;
                            int h = appName.LastIndexOf("bin");
                            string ll = appName.Remove(h);                
                            string g = ll + "Resources\\sample.txt";
                            System.Diagnostics.Process.Start(g);
                        }
                        

                        【讨论】:

                        • 使用 Environment.CurrentDirectory 是非常错误的,不要使用这个!此路径可以在运行时更改。即使在启动时也是不确定的。
                        猜你喜欢
                        • 1970-01-01
                        • 2011-03-14
                        • 1970-01-01
                        • 2017-09-02
                        • 1970-01-01
                        • 2011-06-07
                        • 1970-01-01
                        • 2022-08-02
                        相关资源
                        最近更新 更多