ChatGPT解决这个技术问题 Extra ChatGPT

如何在 .NET 控制台应用程序中获取应用程序的路径?

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

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

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

S
Sebastian Brosch

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
R
Richard Ev

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

AppDomain.CurrentDomain.BaseDirectory

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

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

// 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 如果路径包含 & 或 # 这不起作用
S
Steve Mc

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

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 的答案是虚假的,因为它们仅在执行程序集是可执行文件时才返回可执行文件。这不是简单的,而是正确的解决方案。
r
rocketsarefast

对于任何对 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”物理运行的,所以第一个解决方案绝对不适合网络应用程序。


f
fizzled

上面的答案是我需要的 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
我觉得这是最好的一个。在任何环境中,同样的方法对我来说都是可靠的。在生产中,本地调试,单元测试......想要打开您在单元测试中包含的内容文件(“内容 - 如果较新则复制”)?在那里。
D
Dejan

如果您正在寻找与 .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 的首选替代品


另请参阅 github.com/dotnet/runtime/issues/13051,了解独立的 dotnet 控制台应用程序。这里的建议是使用 Process.GetCurrentProcess().MainModule.FileName
P
Peter Mortensen

您可能希望这样做:

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

请注意,这会带回一个 URI,但并非在所有情况下都支持该 URI。
M
Matas Vaitkevicius

你可以改用这个。

System.Environment.CurrentDirectory

这将获得可执行文件的文件夹
这可以通过多种方式进行更改(快捷方式设置等)......最好不要使用它。
F
F.Alves

对于控制台应用程序,您可以尝试以下操作:

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 可以在运行时设置。不保证正确”
T
Trimantra Software Solution

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

AppDomain.CurrentDomain.BaseDirectory

S
Sol

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

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

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

简单的应用程序

在 Assembly.GetEntryAssembly() 将返回 null 的另一个域中

DLL 作为字节数组从嵌入式资源加载并作为 Assembly.Load(byteArrayOfEmbeddedDll) 加载到 AppDomain

使用 Mono 的 mkbundle 捆绑包(没有其他方法有效)


在 linux 上的调试器下返回:/usr/share/dotnet
f
fruggiero

您可以简单地添加到您的项目引用 System.Windows.Forms,然后像往常一样使用 System.Windows.Forms.Application.StartupPath

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


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

我用过

System.AppDomain.CurrentDomain.BaseDirectory

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


u
user3596865

我的意思是,为什么不使用 ap/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?
H
Herman

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

d
developer747

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

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

这是不正确的,因为您可以在结果中获得随机目录。
此命令返回 Environment.CurrentDirectory,它可能在运行时更改为任何路径,因此它不是一个可靠的解决方案。
N
Nicolas Raoul

在 VB.net

My.Application.Info.DirectoryPath

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


D
DanB

我没有看到有人将 .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 有何不同。您能详细说明一下吗?
N
Nirav Mehta
AppDomain.CurrentDomain.BaseDirectory

将解决问题,以使用安装包引用第 3 方参考文件。


这个答案已经在 5 年前提出,甚至不止一次。
D
Donny V.

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

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

d
daniele3004

试试这个简单的代码行:

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

c
colin lamarre

这些方法在特殊情况下都不起作用,例如使用指向 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) + @"\";
    }
}

D
DanB

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


j
jmik

对于 .NET 6,有 Environment.ProcessPath。

请参阅https://docs.microsoft.com/en-us/dotnet/api/system.environment.processpath?view=net-6.0


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

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

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

C
Community

这是一个适用于 32 位和 64 位应用程序的可靠解决方案。

添加这些参考:

使用 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 等,以及有限的控制,例如 Kill 等。*


P
Perception

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

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 是非常错误的,不要使用这个!此路径可以在运行时更改。即使在启动时,它也是不确定的。