解决网上邻居无法访问时显示打开或另存对话框长时间不响应的思路

C#中使用OpenFileDialog或SaveFileDialog对话框选择文件时,假设A机最后一次访问了B机上的共享目录,之后B机断开了网络,A机再选择文件时软件会长时间不响应。

解决思路是在显示对话框之前先获取最后一次选择文件的目录,然后检查该目录是否可以访问,如果不能访问则设置初始目录为系统当前目录。

经研究发现选择文件对话框在选择文件后会在注册表保存最后一次访问的目录,每个程序都有自己的最后访问目录,对应注册表键是[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedMRU]。

为了获取各个程序最后的访问目录,我写了两个函数,第1个是取得运行程序的程序文件名,参数是程序的命令行参数:

        public string GetAppFileName(string cmdLine)
        {
            string[] parts = cmdLine.Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);         // 以双引号分隔命令行参数
            int lastPos = parts[0].LastIndexOf('\\');

            return parts[0].Substring(lastPos + 1);
        }

第2个函数是传入上面函数返回值,从注册表中取得该程序最后一次浏览访问的目录:

        public string GetLastVisitedMRU(string appName)
        {
            RegistryKey key = OpenSubKey(Registry.CurrentUser,
                @"Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedMRU", false);
            string mruList = key.GetValue("MRUList") as string;

            for (int i = 0; i < mruList.Length; ++i)
            {
                string keyName = mruList.Substring(i, 1);
                string mruKeyValue = null;

                try
                {
                    // Unsafe code section, must be select the 'Allow unsafe code' in Build property
                    unsafe
                    {
                        sbyte[] keyVal = key.GetValue(keyName) as sbyte[];

                        fixed (sbyte* pKeyVal = keyVal)
                        {
                            mruKeyValue = new string(pKeyVal, 0, keyVal.Length, Encoding.Unicode);
                        }
                    }

                    int zeroPos = mruKeyValue.IndexOf('\0');
                    string mruAppName = mruKeyValue.Substring(0, zeroPos);

                    if (mruAppName.Equals(appName, StringComparison.CurrentCultureIgnoreCase))
                    {
                        // Remove the last zero character
                        return mruKeyValue.Substring(zeroPos + 1, mruKeyValue.Length - zeroPos - 2);
                    }
                }
                catch (System.Exception)
                {
                    // Do nothing here
                }
            }

            return string.Empty;
        }

这个函数使用到了unsafe块,所以必须修改程序Build的属性,钩选“Allow unsafe code”。

相关OpenSubKey函数:

        public RegistryKey OpenSubKey(RegistryKey keyRoot, string subKey, bool writable)
        {
            RegistryKey result = keyRoot;
            string[] keys = subKey.Split(new char[] { '\\', '/' });

            foreach (string key in keys)
            {
                result = result.OpenSubKey(key, writable);

                if (result == null)
                {
                    break;
                }
            }

            return result;
        }

最后在显示选择文件对话框之前调用这两个函数并判断:

            OpenFileDialog dlg = new OpenFileDialog();

            string lastVisitedMRU = GetLast
VisitedMRU(GetAppFileName(Environment.CommandLine));

            if (!Directory.Exists(lastVisitedMRU))
            {
                dlg.InitialDirectory = Environment.CurrentDirectory;
            }
           
            if (dlg.ShowDialog(this) == DialogResult.OK) { ... }

使用过程虽然可以自动判断并切换选择文件对话框的初始目录,但发现调用Directory.Exists()判断网上邻居的目录是否可以访问时仍耗时比较长,有待进一步研究解决。

Leave a Reply


提醒: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。请务必注意user必须和评论者名相匹配(大小写一致)。