.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 目录下,然后运行它:
fdee00bbcbbc907685d3abf60b47e799.png其中按钮的事件代码如下:

private void Button_Click(object sender, RoutedEventArgs e){var process = new Process();process.StartInfo.FileName = this.path.Text;process.Start();}

点击这个按钮,它会把我们的测试程序 basedir.exe 给运行起来:
fe81ced297ea2e84c26c799f5c99c4ba.png
我们可以看到,当 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 APPSC:\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 测试程序:
3e18dd5e91250ab3d0149277f355c097.png

40c96a70b2c8bfcf19666ab6cfce15ae.png

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

总结

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

ae5118808ea0294caf1fa23f69e3e73e.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288602.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux之sed命令

1 sed命令(stream edit) 我们一般用来替换文件内容 2 常用方法 1)、比如一般用sed命令替换文件内容,这里参数-i的意思是操作后直接保存在file里面去了 sed -i s/pattern/replace_str/ file 比如我们把sed.txt文件里面的chenyu替换成chen cat sed.txt chenyu ch…

Liststring[] 如何去重

List<string[]> 如何去重&#xff0c;代码如下: static void Main(string[] args){List<string[]> list new List<string[]>();list.Add(new string[] { "1", "2", "3" });list.Add(new string[] { "1" });list.Ad…

sql create,not null 简单用法(mysql)

sql中CREATE用于创建&#xff0c;一般所说的CREATE DATABASE dbname就是创建数据库&#xff0c;那么dbname我们换成我们所要创建的数据库名称即可。 例如创建一个test数据库&#xff1a; CREATE DATABASE test 创建一个表&#xff1a; CREATE TABLE TableName ( 数据库列…

【译】《Understanding ECMAScript6》- 第三章-Object

目录 Object分类Object字面量扩展Object.assign()重复属性改变原型super引用方法总结ES6针对Object的改进&#xff0c;旨在使JavaScript语言更加接近“万物皆对象”的理念。随着越来越多地使用Object类型进行开发&#xff0c;开发者们越来越不满足于Object相对低下的开发效率。…

php邮件发送tp,Thinkphp5 邮件发送Thinkphp发送邮件

在项目的开发中 用户修改密码&#xff0c;需要发送验证码到用户邮箱&#xff0c;在common.php公共文件中加入以下代码&#xff1a;/*** 系统邮件发送函数* param string $tomail 接收邮件者邮箱* param string $name 接收邮件者名称* param string $subject 邮件主题* param st…

数据库面试题目经典大全

1、事务 事务是指一个工作单元&#xff0c;它包含了一组数据操作命令&#xff0c;并且所有的命令作为一个整体一起向系统提交或撤消请求操作&#xff0c;即这组命令要么都执行&#xff0c;要么都不执行。例如&#xff0c;网上购物的交易过程至少包括以下几个步骤的操作&…

物联网离风口还差最关键一环?

物联网智能时代所带来的不仅仅是物物相联的机会&#xff0c;更是会彻底改变用户和企业之间的关系。用户和硬件、用户和服务之间会更紧密联结在一起。众多业内人士纷纷表示&#xff0c;万物互联的前景虽然美好&#xff0c;但缺乏标准已经成为阻碍产业发展的最大瓶颈。 现阶段痛点…

python创建不可变集合_python不可变集合是什么

在很多的其他语言中在传递参数的时候允许程序员选择值传递还是引用传递(比如c语言加上*号传递指针就是引用传递&#xff0c;而直接传递变量名就是值传递)&#xff0c;而python只允许使用引用传递&#xff0c;但是它加上了可变类型和不可变类型&#xff0c;让我们感觉有点混乱了…

前端大屏模板分享-可在线浏览

1. 前言站长以前介绍过这个开源项目&#xff0c;最近又有人在问&#xff0c;索性挂在Dotnet9网站上&#xff0c;方便大家在线浏览&#xff0c;先声明&#xff0c;模板来自下面的仓库&#xff1a;仓库名&#xff1a;大屏数据展示模板作者&#xff1a;lvyeyou开源协议&#xff1a…

linux shell之awk

1 awk awk 是一款设计用于数据流的工具&#xff0c; awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处 awk 脚本的结构基本如下所示: awk BEGIN{print "start" } pattern { commands } END{ print "end" } file awk 脚本通常由3部分组成…

微软的最高市值是多少?

有人说微软1999 年 12 一个月股价达到历史新高 $58.38不准确。我1999年12月22可能会增加微软。公司按照1999年12月27最新价格格&#xff08;119.125&#xff0c;股票分割后成为59.5625&#xff09;他给了我一个选项。价格格&#xff0c;微软股价史无前例的成为了最高点&#xf…

使用Scala实现Java项目的单词计数:串行及Actor版本

其实我想找一门“具有Python的简洁写法和融合Java平台的优势&#xff0c; 同时又足够有挑战性和灵活性”的编程语言。 Scala 就是一个不错的选择。 Scala 有很多语言特性&#xff0c; 建议先掌握基础常用的&#xff1a; 变量、控制结构 、正则与模式匹配、集合、文件读写/目录遍…

vnc oracle solaris,在Solaris下安装VNC 远程安装Oracle

最近在折腾oracle ebs的安装 &#xff0c;用xmanager连接solairs10的过程中&#xff0c;总是连接3个小时左右就挂掉&#xff0c;很郁闷&#xff01;本文参考eygle的文章1&#xff1b;首先从 eygle提供的网址上下载vnc软件下载得到的文件如下 vnc-3.3.4-solaris_2.5.tar.tar然…

sql CHECK ,UNIQUE 约束(mysql)

check 用来限定值的范围&#xff0c;如下表&#xff1a; CREATE TABLE test22 ( age INT(10), sex VARCHAR(10), name11 VARCHAR(10) NOT NULL, CHECK (age>0) ) 在此&#xff0c;check限制了age的值为0以上 如果想让age的的不唯一呢&#xff1f;那就使用UNIQUE了&…

html+css常用小笔记(持续更新)

1去掉input点击时的蓝色边框outline:none; 2禁止文本选中-webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Chrome/Safari/Opera */ -khtml-user-select: none; /* Konqueror */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /…

音视频基本概念和FFmpeg的简单入门

写在前面最近正好有音视频编辑的需求&#xff0c;虽然之前粗略的了解过FFmpeg不过肯定是不够用的&#xff0c;借此重新学习下&#xff1b;基本概念容器/文件(Conainer/File)&#xff1a;即特定格式的多媒体文件&#xff0c;一般来说一个视频文件是由视频&#xff0c;音频&#…

linux shell之paste合并文件和找到匹配的文件里面替换内容(find和-exec或xargs组合)

1 问题 1&#xff09;合并2个文件&#xff0c;这里用paste命令 2&#xff09;找到匹配的文件里面替换内容&#xff0c;这里用find 和 -exec或xargs命令组合 2 实现 1&#xff09;合并2个文件&#xff0c;这里用paste命令&#xff0c;我们在paste后面加参数-d 然后加" &…

Windows Live Writer 的昨日荣光

今天这一篇文章&#xff0c;想写一写Windows Live Writer这款博客编辑器&#xff08;最早的一个版本是2007年发布的&#xff09;。毫不夸张地说&#xff0c;这是为数不多的几款所见即所得的编辑器之一&#xff0c;当然&#xff0c;它的运行速度慢也是一个众所周知的问题。作为一…

qpython执行手机脚本精灵使用教程_Android上执行python脚本-QPython

看书&#xff0c;发现android可以跑python。尝试了一下。首先需要在手机上安装python环境&#xff0c;通过安装apk实现&#xff0c;这个apk叫QPython&#xff0c;还有同类的比如SL4A。QPython的官网&#xff1a;https://www.qpython.com/可以在官网上下载QPython的安装包&#…

猎豹MFC--CMenu菜单 设置主菜单 给主对话框设置菜单 设置快捷菜单

设置主菜单&#xff08;不是快捷菜单&#xff09;&#xff1a;给主对话框设置菜单&#xff1a;效果如下&#xff1a;修改菜单的ID使之便于记忆&#xff1a;给菜单添加消息处理&#xff1a;添加处理代码&#xff1a;设置快捷菜单&#xff1a;打开对话框&#xff0c;属性添加消息…