前些天有 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.BaseDirectoryAppContext.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.GetCurrentDirectoryEnvironment.CurrentDirectory 方法输出均为 c:\APPS 而其它方法则都输出了 exe 所在目录。

使用另外一个 exe 程序启动测试程序

同样我们再次使用另外一个 WPF 程序来运行 basedir.exe 测试程序:

可以看到 Directory.GetCurrentDirectoryEnvironment.CurrentDirectory 方法输出均为 c:\APPS,也就是 WPF 所在的目录, 而其它方法则都输出了 exe 所在目录。

总结

以上常见的 5 种读取程序当前目录的办法在绝大多数情况下都可以正确的获取到预期的结果。其中需要注意的是Directory.GetCurrentDirectoryEnvironment.CurrentDirectory。这2个方法在 cmd 或者 bash 环境下返回的是工作目录;使用 A 程序启动另外一个 B 程序的时候,B 程序获取到的根目录是 A 程序所在的目录。所以使用 Directory.GetCurrentDirectoryEnvironment.CurrentDirectory 的时候一定要格外注意,避免引入 BUG 。

关注我的公众号一起玩转技术

.NET 程序读取当前目录避坑指南的更多相关文章

  1. 今天 1024,为了不 996,Lombok 用起来以及避坑指南

    Lombok简介.使用.工作原理.优缺点 Lombok 项目是一个 Java 库,它会自动插入编辑器和构建工具中,Lombok 提供了一组有用的注解,用来消除 Java 类中的大量样板代码. 目录 L ...

  2. electron 编译 sqlite3避坑指南---尾部链接有已经编译成功的sqlite3

    electron 编译 sqlite3避坑指南(尾部链接有已经编译成功的sqlite3) sqlite很好用,不需要安装,使用electron开发桌面程序,sqlite自然是存储数据的不二之选,奈何编 ...

  3. CEF避坑指南(一)——下载并编译第一个示例

    CEF即Chromium Embedded Framework,Chrome浏览器嵌入式框架.它提供了接口供程序员们把Chrome放到自己的程序中.许多大型公司,如网易.腾讯都开始使用CEF进行前端开 ...

  4. Canal v1.1.4版本避坑指南

    前提 在忍耐了很久之后,忍不住爆发了,在掘金发了条沸点(下班时发的): 这是一个令人悲伤的故事,这条情感爆发的沸点好像被屏蔽了,另外小水渠(Canal意为水道.管道)上线一段时间,不出坑的时候风平浪静 ...

  5. Harmony OS 开发避坑指南——源码下载和编译

    Harmony OS 开发避坑指南--源码下载和编译 本文介绍了如何下载鸿蒙系统源码,如何一次性配置可以编译三个目标平台(Hi3516,Hi3518和Hi3861)的编译环境,以及如何将源码编译为三个 ...

  6. Linux下Python3.6的安装及避坑指南

    Python3的安装 1.安装依赖环境 Python3在安装的过程中可能会用到各种依赖库,所以在正式安装Python3之前,需要将这些依赖库先行安装好. yum -y install zlib-dev ...

  7. Hive改表结构的两个坑|避坑指南

    Hive在大数据中可能是数据工程师使用的最多的组件,常见的数据仓库一般都是基于Hive搭建的,在使用Hive时候,遇到了两个奇怪的现象,今天给大家聊一下,以后遇到此类问题知道如何避坑! 坑一:改变字段 ...

  8. Android连接远程数据库的避坑指南

    Android连接远程数据库的避坑指南 今天用Android Studio连接数据库时候,写了个测试连接的按钮,然后连接的时候报错了,报错信息: 2021-09-07 22:45:20.433 705 ...

  9. Windows环境下Anaconda安装TensorFlow的避坑指南

    最近群里聊天时经常会提到DL的东西,也有群友在学习mxnet,但听说坑比较多.为了赶上潮流顺便避坑,我果断选择了TensorFlow,然而谁知一上来就掉坑里了…… 我根据网上的安装教程,默认安装了最新 ...

随机推荐

  1. 【论文阅读】CVPR2021: MP3: A Unified Model to Map, Perceive, Predict and Plan

    Sensor/组织: Uber Status: Reading Summary: 非常棒!端到端输出map中间态 一种建图 感知 预测 规划的通用框架 Type: CVPR Year: 2021 引用 ...

  2. 详细描述一下 Elasticsearch 索引文档的过程 ?

    面试官:想了解 ES 的底层原理,不再只关注业务层面了. 解答: 这里的索引文档应该理解为文档写入 ES,创建索引的过程. 文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流 ...

  3. 为什么要用 Dubbo?

    随着服务化的进一步发展,服务越来越多,服务之间的调用和依赖关系也越来越 复杂,诞生了面向服务的架构体系(SOA), 也因此衍生出了一系列相应的技术,如对服务提供.服务调用.连接处理.通信 协议.序列化 ...

  4. 说说do...while和while的区别

    一.do-while语句 do-while语句的语法: do{ statement }while(expression); 看下面示例: var i=10: do{ i+=2: }while(i< ...

  5. 怎么将 byte 转换为 String?

    可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用 的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也 可能不同.

  6. HTML 5中的输出元素是什么?

    当你需要计算两个输入的结果并将结果放到一个标签里的时候,就需要输出元素了.比如你有两个文本框(参见下图),你想要让这些文本框数字相加,然后输出给标签. 下面就是如何使用HTML 5代码输出元素. &l ...

  7. C++面向对象 - 类的前向声明的用法

    C++中的类应当是先定义,然后使用.但在处理相对复杂的问题,比如考虑类的组合时,有可能遇到两个类相互引用的情况,这种情况称为循环依赖. 考虑下面代码: class A { public: void f ...

  8. 几种常见布局的flex写法

    flex布局目前基本上兼容主流的浏览器,且实现方式简单.我整理了flex的一些知识点,并且总结归纳了几种常见布局的flex写法 ​flex基础知识点 flex-grow和flex-shrink相关计算 ...

  9. oracle 序列的使用

    序列: 是oacle提供的用于产生一系列唯一数字的数据库对象. 自动提供自增的唯一的数值. 共享的数据 主要用于提供主键值 将序列装入内存可以提高访问效率 1.创建序列: 1.  要有创建序列的权限 ...

  10. 正则系列——JavaScript正则表达式入门心得

    我发现有个别字符被这个编辑器给刷掉了,但是灰色区域显示正常,以灰色区域代码为准 什么玩意? 在我刚开始学习编程的时候,就听过正则了,也听说正则很牛逼,懂正则的更牛逼.但是苦于没有人指点,也没有使用正则 ...