.NET 程序读取当前目录避坑指南
前些天有 AgileConfig 的用户反映,如果把 AgileConfig 部署成 Windows 服务程序会启动失败。我看了一下日志,发现根目录被定位到了 C:\Windows\System32 下,那么读取 appsettings.json 配置文件自然就失败了。
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory());
以上是我构造 ConfigurationBuilder 的代码。使用 Directory.GetCurrentDirectory() 获取程序根目录然后设置 SetBasePath 。以上代码在99%的情况是不会有问题的,那么为什么会在做为服务部署的时候会有问题呢?让我们往下看。
Directory.GetCurrentDirectory()
Directory.GetCurrentDirectory() 获取根目录是我们很常见的一个操作。先让我们对其进行一些简单的测试。新建一个控制台程序,编写以下代码进行:
var dirpath = Directory.GetCurrentDirectory();
Console.WriteLine("Directory.GetCurrentDirectory = " + dirpath);
直接运行
代码很简单,就是读取根目录,然后输出一下。
Directory.GetCurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
编译完成后双击 exe 文件,可以看到获取到的目录是正确的。
使用 cmd 运行
下面让我们试一下在 cmd 下运行这个 exe 。
C:\>cd C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0>basedir.exe
Directory.GetCurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
我们把路径切换到 exe 所在的目录,然后输入 basedir.exe 直接运行它,可以看到输出的目录也是正确的。
切换工作目录
这次我们把工作目录切换到 C 盘的 apps 目录,然后使用完全路径去执行 exe 程序。
C:\>cd apps
C:\APPS>C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\basedir.exe
Directory.GetCurrentDirectory = C:\APPS
怎么样?是不是跟预期的不一样了?这次输出的根目录不是 exe 所在的目录,而是 c:\APPS ,也就是我们的工作目录。
使用另外一个 exe 程序启动测试程序
在我们日常场景中有很多时候需要通过一个程序去运行另外一个程序,那么这个时候 Directory.GetCurrentDirectory 获取的根目录是怎么样的呢?
首先我们编写另外一个 WPF 的程序,使用这个程序来启动我们的 basedir.exe 测试程序。
我们把这个 WPF 程序放在 c:\APPS 目录下,然后运行它:

其中按钮的事件代码如下:
private void Button_Click(object sender, RoutedEventArgs e)
{
var process = new Process();
process.StartInfo.FileName = this.path.Text;
process.Start();
}
点击这个按钮,它会把我们的测试程序 basedir.exe 给运行起来:

我们可以看到,当 WPF 程序把我们的测试程序运行起来的时候,测试程序输出的目录为 c:\APPS,也就是 WPF 程序所在的目录。
为什么做为服务运行的时候获取程序根目录为 System32
通过以上的测试,AgileConfig 做为服务运行的时候获取根目录为 C:\Windows\System32 的原因已经很明显了。我们的 windows 服务的启动一般来说有2个途径,一个是通过 sc.exe 工具另外一个是通过 services.exe 也就是 SCM (service control manager) 来启动。那么这2个可执行程序在哪里呢?答案就是 C:\Windows\System32 。我们的 windows 服务的启动其实是通过这2个工具来运行的,所以根据上面的测试,很明显通过Directory.GetCurrentDirectory来获取根目录的话会是这2个工具所在的目录。
其它读取程序根目录的方式
通过以上我们知道通过Directory.GetCurrentDirectory读取根目录会有一点小坑。在我们的 .NET 世界里还有很多办法能获取根目录,那么他们会不会也有坑呢?
以下列几个常见的获取根目录的方法:
var dirpath = Directory.GetCurrentDirectory();
Console.WriteLine("Directory.GetCurrentDirectory = " + dirpath);
// 通过 AppDomain.CurrentDomain.BaseDirectory 读取根目录
var dirpath1 = AppDomain.CurrentDomain.BaseDirectory;
Console.WriteLine("AppDomain.CurrentDomain.BaseDirectory = " + dirpath1);
// 通过 Environment.CurrentDirectory 来读取根目录
var dirpath2 = Environment.CurrentDirectory;
Console.WriteLine("Environment.CurrentDirectory = " + dirpath2);
// 通过 Assembly.GetExecutingAssembly().Location 来获取运行程序集所在的位置,从而判断根目录
var dirpath3 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Console.WriteLine("Path.GetDirectoryName Assembly.GetExecutingAssembly().Location = " + dirpath3);
// 通过 AppContext.BaseDirectory 获取根目录
var dirpath4 = AppContext.BaseDirectory;
Console.WriteLine("AppContext.BaseDirectory = " + dirpath4);
直接运行
把以上获取根目录的代码补充进我们的测试程序,编译成功后直接运行:
Directory.GetCurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
AppDomain.CurrentDomain.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
Environment.CurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
Path.GetDirectoryName Assembly.GetExecutingAssembly().Location = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
AppContext.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
以上是输出结果。可以看到所有的方法都准确的获取到了 exe 程序所在的根目录。有一点要注意的是
AppDomain.CurrentDomain.BaseDirectory 跟 AppContext.BaseDirectory 方法获取的路径最后带有一个 \ 其它则没有。
使用 cmd 运行
同样让我们在 cmd 下运行一下:
C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0>basedir.exe
Directory.GetCurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
AppDomain.CurrentDomain.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
Environment.CurrentDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
Path.GetDirectoryName Assembly.GetExecutingAssembly().Location = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
AppContext.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
我们把路径切换到 exe 所在的目录,然后输入 basedir.exe 直接运行它,可以看到所有的方法输出的目录都是正确的。
切换工作目录
同样我们在 cmd 下把工作目录切换到 c:\APPS ,然后运行 exe 。
C:\>cd APPS
C:\APPS>C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\basedir.exe
Directory.GetCurrentDirectory = C:\APPS
AppDomain.CurrentDomain.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
Environment.CurrentDirectory = C:\APPS
Path.GetDirectoryName Assembly.GetExecutingAssembly().Location = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0
AppContext.BaseDirectory = C:\00WORKSPACE\basedir\basedir\bin\Debug\net6.0\
可以看到 Directory.GetCurrentDirectory 和 Environment.CurrentDirectory 方法输出均为 c:\APPS 而其它方法则都输出了 exe 所在目录。
使用另外一个 exe 程序启动测试程序
同样我们再次使用另外一个 WPF 程序来运行 basedir.exe 测试程序:


可以看到 Directory.GetCurrentDirectory 和 Environment.CurrentDirectory 方法输出均为 c:\APPS,也就是 WPF 所在的目录, 而其它方法则都输出了 exe 所在目录。
总结
以上常见的 5 种读取程序当前目录的办法在绝大多数情况下都可以正确的获取到预期的结果。其中需要注意的是Directory.GetCurrentDirectory 和 Environment.CurrentDirectory。这2个方法在 cmd 或者 bash 环境下返回的是工作目录;使用 A 程序启动另外一个 B 程序的时候,B 程序获取到的根目录是 A 程序所在的目录。所以使用 Directory.GetCurrentDirectory 和 Environment.CurrentDirectory 的时候一定要格外注意,避免引入 BUG 。
关注我的公众号一起玩转技术

.NET 程序读取当前目录避坑指南的更多相关文章
- 今天 1024,为了不 996,Lombok 用起来以及避坑指南
Lombok简介.使用.工作原理.优缺点 Lombok 项目是一个 Java 库,它会自动插入编辑器和构建工具中,Lombok 提供了一组有用的注解,用来消除 Java 类中的大量样板代码. 目录 L ...
- electron 编译 sqlite3避坑指南---尾部链接有已经编译成功的sqlite3
electron 编译 sqlite3避坑指南(尾部链接有已经编译成功的sqlite3) sqlite很好用,不需要安装,使用electron开发桌面程序,sqlite自然是存储数据的不二之选,奈何编 ...
- CEF避坑指南(一)——下载并编译第一个示例
CEF即Chromium Embedded Framework,Chrome浏览器嵌入式框架.它提供了接口供程序员们把Chrome放到自己的程序中.许多大型公司,如网易.腾讯都开始使用CEF进行前端开 ...
- Canal v1.1.4版本避坑指南
前提 在忍耐了很久之后,忍不住爆发了,在掘金发了条沸点(下班时发的): 这是一个令人悲伤的故事,这条情感爆发的沸点好像被屏蔽了,另外小水渠(Canal意为水道.管道)上线一段时间,不出坑的时候风平浪静 ...
- Harmony OS 开发避坑指南——源码下载和编译
Harmony OS 开发避坑指南--源码下载和编译 本文介绍了如何下载鸿蒙系统源码,如何一次性配置可以编译三个目标平台(Hi3516,Hi3518和Hi3861)的编译环境,以及如何将源码编译为三个 ...
- Linux下Python3.6的安装及避坑指南
Python3的安装 1.安装依赖环境 Python3在安装的过程中可能会用到各种依赖库,所以在正式安装Python3之前,需要将这些依赖库先行安装好. yum -y install zlib-dev ...
- Hive改表结构的两个坑|避坑指南
Hive在大数据中可能是数据工程师使用的最多的组件,常见的数据仓库一般都是基于Hive搭建的,在使用Hive时候,遇到了两个奇怪的现象,今天给大家聊一下,以后遇到此类问题知道如何避坑! 坑一:改变字段 ...
- Android连接远程数据库的避坑指南
Android连接远程数据库的避坑指南 今天用Android Studio连接数据库时候,写了个测试连接的按钮,然后连接的时候报错了,报错信息: 2021-09-07 22:45:20.433 705 ...
- Windows环境下Anaconda安装TensorFlow的避坑指南
最近群里聊天时经常会提到DL的东西,也有群友在学习mxnet,但听说坑比较多.为了赶上潮流顺便避坑,我果断选择了TensorFlow,然而谁知一上来就掉坑里了…… 我根据网上的安装教程,默认安装了最新 ...
随机推荐
- 统计分析— 1.SPSS数据编辑窗口 输出窗口 语法窗口
第一课-SPSS窗口 一 数据编辑窗口(Data Editor) 二 输出窗口(Output Viewer ) 三 语法窗口(Syntax Editor):针对中高级用户,有些操作可以通过输入代码的方 ...
- react单向数据流怎么理解?
React是单向数据流,数据主要从父节点传递到子节点(通过props).如果顶层(父级)的某个props改变了,React会重渲染所有的子节点.
- Spring Cloud与Spring Boot版本匹之间的关系
由于学习的起步较晚,创建项目的时候一直采用的都是较新的springboot,用的2.0.2.RELEASE版本.参照网上的示例进行实验的时候,有时候会才坑,特记录一二以备忘 首先就是SpringBoo ...
- 您使用了哪些 starter maven 依赖项?
使用了下面的一些依赖项 spring-boot-starter-activemq spring-boot-starter-security 这有助于增加更少的依赖关系,并减少版本的冲突.
- Error running 'App': Command line is too long. Shorten command line for App or also for Spring Boot default configuration.
找到标签 <component name="PropertiesComponent">.在标签里加一行 : <property name="dynam ...
- 实践中如何优化 MySQL ?
最好是按照以下顺序优化: 1.SQL 语句及索引的优化 2.数据库表结构的优化 3.系统配置的优化 4.硬件的优化 详细可以查看 阿里 P8 架构师谈:MySQL 慢查询优化.索引优化.以及表等优化
- MyBatis 实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询.联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面的 collection 节点配置一对多的类就可以完成:嵌套查询是先查 一个表,根据这个表里面的 结果的外键 ...
- Java中final的使用
原文链接https://www.cnblogs.com/dolphin0520/p/10651845.html 作者Matrix海 子 本文为笔记 0. 概述 final和static一样都是修饰词, ...
- 6_稳定性_李雅普诺夫_Lyapunov
李雅普诺夫方法参考
- 3.3V转5V原理图