至于C#中先后当前目录的小小说

【题外话】
近年做了个.NET
4.0阳台的次序,一直在Win7/8上运行的佳绩的,结果用户说XP上说有标题,于是自己就改了下程序,伸张了记log的效果然后发给用户。log的目录是根据Environment.CurrentDirectory得出的。必要用户运行完程序之后将log发回给自己,但用户一向找不到这几个文件。

 

【文章索引】

  1. 殊不知的目录
  2. 奇怪的RestoreDirectory
  3. 系统的题材
  4. 此外获取方式

 

【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,如下图。

图片 1

图片 2

图中标题为OpenFileDialog默许的RestoreDirectory的值,文本框中第二个途径为OpenFileDialog.Show以前的Environment.CurrentDirectory,第四个途径为Show之后的不二法门,测试的时候均手动修改了RestoreDirectory的值(即每组第二个图手动设置为false,首个图设置为true),测试代码如下:

 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上的参数创立,结果如下图。

图片 3

图片 4

依旧与在.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通过此赢得的目录一定是程序所在的目录。

 

【相关链接】

  1. Environment.CurrentDirectory
    属性:http://msdn.microsoft.com/zh-cn/library/system.environment.currentdirectory.aspx.aspx)
  2. OpenFileDialog在XP會更改working
    directory:http://swaywang.blogspot.com/2012/06/copenfiledialogxpworking-directory.html

相关文章