最稳妥方式是AppDomain.CurrentDomain.BaseDirectory,单文件发布时需回退到Process.GetCurrentProcess().MainModule.FileName(Windows)或/proc/self/exe(Linux),并统一做Path.GetFullPath()标准化。

获取当前 EXE 所在目录用 AppDomain.CurrentDomain.BaseDirectory
这是最稳妥的方式,返回的是启动该 .NET 进程的 .exe 文件所在文件夹(不含文件名),无论程序是双击运行、命令行启动,还是被其他进程加载,只要没手动修改 BaseDirectory,它就指向 EXE 目录。
常见错误是误用 Environment.CurrentDirectory —— 它返回的是“当前工作目录”,可能被 Directory.SetCurrentDirectory() 改过,也可能随 Shell 启动路径变化,和 EXE 位置完全无关。
-
AppDomain.CurrentDomain.BaseDirectory总是以反斜杠\结尾,直接拼接文件名即可,比如Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json") - 在 .NET Core / .NET 5+ 中依然有效,但注意:如果应用是单文件发布(
PublishSingleFile=true),BaseDirectory指向的是解压临时目录,不是原始 EXE 位置 —— 此时得换方法 - 不要用
Assembly.GetExecutingAssembly().Location去取路径再Path.GetDirectoryName(),因为热重载、AOT 编译或某些部署方式下Location可能为空字符串
单文件发布时必须用 Process.GetCurrentProcess().MainModule.FileName
当项目启用单文件发布(dotnet publish -p:PublishSingleFile=true),BaseDirectory 指向的是运行时解压路径(如 C:\Users\...\AppData\Local\Temp\.net\MyApp\...),不是你期望的安装目录。这时要回退到 Win32 层面,靠进程主模块定位原始 EXE。
这个方法只适用于 Windows;Linux/macOS 需走 /proc/self/exe 或 _NSGetExecutablePath,但跨平台建议统一用 System.Reflection.Assembly.GetExecutingAssembly().Location + 回退逻辑(见下一点)。
-
Process.GetCurrentProcess().MainModule.FileName返回完整路径(含文件名),记得用Path.GetDirectoryName()提取目录 - 需要
System.Diagnostics命名空间,且调用时可能抛Win32Exception(权限不足或沙盒环境),务必 try-catch - 在某些容器或受限环境中(如 GitHub Codespaces、Azure App Service 沙盒),
MainModule可能不可读,此时应 fallback 到其他策略
Assembly.GetExecutingAssembly().Location 的适用边界
这个方法返回当前正在执行的程序集(DLL 或 EXE)的磁盘路径。对传统框架程序(非单文件、非 AOT)来说,它和 EXE 路径一致;但要注意:它返回的是“被 JIT 加载的程序集路径”,不一定是源 EXE —— 比如你从 bin\Debug\ 运行,它返回的就是那个路径;但如果用 VS 调试,实际加载的是 bin\Debug\net6.0\ 下的输出,路径就不同了。
- 在调试模式下,
Location指向的是输出目录(如bin\Debug\net6.0\),不是项目根目录,别拿来读配置文件 - .NET 6+ 启用
TrimMode=partial或 AOT 编译后,Location可能为string.Empty,需判空 - 若想兼容调试/发布/单文件三种场景,推荐组合写法:
string exeDir = string.IsNullOrEmpty(Assembly.GetExecutingAssembly().Location) ? AppDomain.CurrentDomain.BaseDirectory : Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
别把 Application.StartupPath 当通用解法
这个属性只在 Windows Forms 应用中可用,且依赖于 System.Windows.Forms.dll。它等价于 AppDomain.CurrentDomain.BaseDirectory,但多了 UI 框架层依赖 —— 控制台、ASP.NET Core、WPF、MAUI 项目里根本不存在这个类型。
有人看到旧博客用它就照抄,结果新建一个控制台项目编译不过,报错 The name 'Application' does not exist in the current context。
- 除非你明确在 WinForms 项目里,否则不要引入
System.Windows.Forms就为用这一个属性 - 即使在 WinForms 中,它也不处理单文件场景,仍需额外判断
- 更隐蔽的坑:
Application.StartupPath在 ClickOnce 部署下返回的是部署缓存路径,不是安装目录
真正麻烦的不是选哪个 API,而是同一份代码要在调试、CI 构建、单文件发布、ClickOnce、容器化多种环境下都返回预期路径。最安全的做法是:优先用 BaseDirectory,单文件时 fallback 到 MainModule.FileName(Windows)或 /proc/self/exe(Linux),并始终对路径做 Path.GetFullPath() 标准化,避免相对路径歧义。










