关于C#中程序当前目录的小随笔
来源:互联网 发布:葡萄牙语翻译软件 编辑:程序博客网 时间:2024/05/16 19:07
【题外话】
最近做了个.NET 4.0平台的程序,一直在Win7/8上运行的好好的,结果用户说XP上说有问题,于是我就改了下程序,增加了记log的功能然后发给用户。log的目录是根据Environment.CurrentDirectory得出的。结果要求用户运行完程序以后将log发回给我,但用户始终找不到这个文件。
【文章索引】
- 奇怪的目录
- 奇怪的RestoreDirectory
- 系统的问题
- 其他获取方式
【1、奇怪的目录】
我的程序打开后需要先用OpenFileDialog打开一个文件,之后开始记录日志,由于之前习惯使用Environment.CurrentDirectory获取程序的当前路径,所以这次也理所当然地把日志文件放在这个目录之下。这个目录在Win7/Win8下一直都是程序运行的目录,但是在XP下却不是。
从MSDN上可以找到Environment.CurrentDirectory作用的说明,一种是“获取和设置当前目录(即该进程从中启动的目录)的完全限定路径.”(.NET Framework 2.0),另一种是“获取或设置当前工作目录的完全限定路径.”(.NET Framework 3.5+)。初一看感觉这俩说明完全是不同的,但仔细想想也不可能出现不同,因为.NET 3.5和2.0不仅仅是同一个CLR,并且Environment.CurrentDirectory更是位于mscorlib.dll中,应该不会出现.NET 3.5和2.0不同的问题。
带着好奇心,我去翻看mscorlib.dll的源代码,发现Environment.CurrentDirectory是这么写的:
1 public static String CurrentDirectory { 2 [ResourceExposure(ResourceScope.Machine)] 3 [ResourceConsumption(ResourceScope.Machine)] 4 get{ 5 return Directory.GetCurrentDirectory(); 6 } 7 8 [ResourceExposure(ResourceScope.Machine)] 9 [ResourceConsumption(ResourceScope.Machine)]10 set {11 Directory.SetCurrentDirectory(value);12 }13 }
曾经从网上看到过一些介绍.NET中获取当前目录方法的文章,其中关于IO.Directory.GetCurrentDirectory()的描述基本上都是获取程序的工作目录,或者有的文章也说不清除获取的是什么。但对于Environment.CurrentDirectory获取的内容,基本上都说是程序当前目录,或者直接照搬MSDN上的说明。
从上述源代码看,其实这俩获取到的是一样的内容。而IO.Directory.GetCurrentDirectory()的代码则如下所示:
1 [ResourceExposure(ResourceScope.Machine)] 2 [ResourceConsumption(ResourceScope.Machine)] 3 public static String GetCurrentDirectory() 4 { 5 StringBuilder sb = new StringBuilder(Path.MAX_PATH + 1); 6 if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0) 7 __Error.WinIOError(); 8 String currentDirectory = sb.ToString(); 9 if (currentDirectory.IndexOf('~') >= 0) {10 //省略部分代码11 } 12 String demandPath = GetDemandDir(currentDirectory, true);13 new FileIOPermission( FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false ).Demand();14 return currentDirectory;15 }
从代码里看到,GetCurrentDirectory其实调用的WIN32 API获取的当前目录,其调用的是kernel32.dll中的GetCurrentDirectory()。
1 [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]2 [ResourceExposure(ResourceScope.Machine)]3 internal static extern int GetCurrentDirectory(int nBufferLength, StringBuilder lpBuffer);
可见,通过Environment.CurrentDirectory或者Directory.GetCurrentDirectory()获取到的并不是由.NET控制的返回值,而是操作系统返回的。同时,这个属性本身就不是程序的启动目录,而应当是程序的工作目录,比如在快捷方式的属性中是可以修改这个路径的。
【2、奇怪的RestoreDirectory】
刚才从GetCurrentDirectory()入手我们只能知道这个值由系统控制,但并不能得知到底出了什么问题。不过,既然是因为使用OpenFileDialog后导致当前目录发生了变化,那就从OpenFileDialog入手,网上也有类似的文章,见相关链接2。
OpenFileDialog有一个很有意思的属性,叫RestoreDirectory,MSDN上的说明是“获取或设置一个值,该值指示对话框在关闭前是否还原当前目录. (从 FileDialog 继承.)”,而且经过测试发现,RestoreDirectory设置为true以后再执行文件对话框是不会更改当前目录的,所以有人推测Win7默认的RestoreDirectory为true,而XP默认的为false。
但经过测试发现,不论什么操作系统(以2k3和Win8为例),.NET中文件对话框的RestoreDirectory默认值都是False,如下图。
图中标题为OpenFileDialog默认的RestoreDirectory的值,文本框中第一个路径为OpenFileDialog.Show之前的Environment.CurrentDirectory,第二个路径为Show之后的路径,测试的时候均手动修改了RestoreDirectory的值,测试代码如下:
1 private void Form1_Load(object sender, EventArgs e) 2 { 3 this.Text = String.Format("Default [RestoreDirectory]={0}", dlgOpen.RestoreDirectory); 4 5 this.chkRestoreDir.Checked = this.dlgOpen.RestoreDirectory; 6 } 7 8 private void btnTest_Click(object sender, EventArgs e) 9 {10 StringBuilder sb = new StringBuilder();11 12 sb.AppendLine(String.Format("[Environment.CurrentDirectory]={0}", Environment.CurrentDirectory));13 sb.AppendLine(String.Format("[RestoreDirectory]={0}", dlgOpen.RestoreDirectory));14 15 dlgOpen.ShowDialog();16 sb.AppendLine(String.Format("[Dialog.FileName]={0}", dlgOpen.FileName));17 sb.AppendLine(String.Format("[Environment.CurrentDirectory]={0}", Environment.CurrentDirectory));18 19 this.txtInfo.Text = sb.ToString();20 }21 22 private void chkRestoreDir_CheckedChanged(object sender, EventArgs e)23 {24 this.dlgOpen.RestoreDirectory = this.chkRestoreDir.Checked;25 }
【3、系统的问题】
为了验证是不是.NET的问题,我用VC++和MFC又写了一个新的程序来重新测试一遍,参数均按照.NET上的参数创建,结果如下图。
仍然与在.NET上测试的结果相同,看来不是.NET的原因,而是系统的原因了。同时,我又搜索了微软公开的.NET Framework的代码,也并未发现在FileDialog中修改了CurrentDirectory的值。 所以,如果需要使用Environment.CurrentDirectory等获取工作目录,那么最好设置RestoreDirectory为true,以保证在任何平台都没有问题;反之,如果想始终获取当前的目录,那么遇到FileDialog时自己手动SetCurrentDirectory下吧。
附,C++测试用的关键代码:
1 CString info, path, T("True"), F("False"); 2 GetCurrentDirectory(0x105, path.GetBuffer(0x105)); 3 path.ReleaseBuffer(); 4 info = "[Environment.CurrentDirectory]="; 5 info = info + path; 6 info = info + "\r\n[RestoreDirectory]=" + (chkRestoreDir.GetCheck() == TRUE ? T : F); 7 8 CFileDialog *dlgOpen; 9 dlgOpen = new CFileDialog(TRUE, (LPCTSTR)"", (LPCTSTR)"", OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | (chkRestoreDir.GetCheck() ? OFN_NOCHANGEDIR : 0), (LPCTSTR)"");10 ;11 if (dlgOpen->DoModal() == IDOK)12 {13 CString fileName;14 fileName = dlgOpen->GetPathName();15 info = info + "\r\n[Dialog.FileName]=" + fileName;16 }17 18 delete dlgOpen;19 20 GetCurrentDirectory(0x105, path.GetBuffer(0x105));21 path.ReleaseBuffer();22 23 info = info + "\r\n[Environment.CurrentDirectory]=" + path;24 InfoText = info;25 UpdateData(false);
【4、其他获取方式】
对于WinForm应用程序,其实可以使用System.Windows.Forms.Application.StartupPath来获取程序所在的目录,MSDN上是这么说明的“获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称.”,其实现代码如下:
1 public static string StartupPath { 2 get { 3 if (startupPath == null) { 4 StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH); 5 UnsafeNativeMethods.GetModuleFileName(NativeMethods.NullHandleRef, sb, sb.Capacity); 6 startupPath = Path.GetDirectoryName(sb.ToString()); 7 } 8 Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + startupPath + ") Demanded"); 9 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, startupPath).Demand();10 return startupPath;11 }12 }
而其中的GetModuleFileName其实也是调用的WIN32 API,具体如下:
1 [DllImport(ExternDll.Kernel32, CharSet=CharSet.Auto)]2 public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
而GetModuleFileName其实是获取执行程序的完整路径,Application.StartupPath通过此获取的目录一定是程序所在的目录。
【相关链接】
- Environment.CurrentDirectory 属性:http://msdn.microsoft.com/zh-cn/library/system.environment.currentdirectory.aspx
- OpenFileDialog在XP會更改working directory:http://swaywang.blogspot.com/2012/06/copenfiledialogxpworking-directory.html
<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
- 关于C#中程序当前目录的小随笔
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的一些方法
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的一些方法
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的五种方法
- C#中得到程序当前工作目录与执行目录的一些方法
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- ▲【C#中得到程序当前工作目录和执行目录的一些方法】▲
- C#中得到程序当前工作目录和执行目录的五种方法
- 总结C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的五种方法
- C#中得到程序当前工作目录和执行目录的一些方法
- C#中得到程序当前工作目录和执行目录的五种方法
- BackTrack 5RC3为Firefox安装Flash Player失败的解决办法
- 黑马程序员 C#基础流程控制_条件语句
- asp.net上传图片并生成水印与缩略图的代码
- ADO.NET的对象 一
- LFS7.3 安装过程
- 关于C#中程序当前目录的小随笔
- 选用CEVA DSP,展讯进入3G手机TD-SCDMA基带处理器开发
- 用Python实现QR二维码的生成
- FrameWork4.0的兼容问题 .
- android下的c程序
- 提取字串
- 微软应用架构指南系列文章翻译
- 重载2
- fedora18使用配置(学习笔记1)