如何检索 OpenFileDialog 使用的最后一个文件夹?

Nic*_*k_F 1 .net c# winapi openfiledialog winforms

在我的程序中,我使用 OpenFileDialog 组件并设置了一个初始目录。

但是,如果我决定从另一个文件夹打开文件,下次我想打开文件时,OpenFileDialog 会记住我用来打开文件的最后一个文件夹,它将打开该文件夹,而不是 InitialDirectory 中指定的文件夹。我很高兴事情是这样的。即使我关闭并重新打开应用程序,最后使用的文件夹仍然会被记住。

有没有办法找出哪个是文件夹?比方说,当我单击按钮时,我希望最后的路径显示在标签中。

OpenFileDialog ofd = new OpenFileDialog();
ofd.InitialDirectory = @"C:\My Initial Folder";
Run Code Online (Sandbox Code Playgroud)

Jim*_*imi 5

免责声明
\n不支持此处描述的过程:任何内容都可能随时更改,恕不另行通知。

\n
\n

\xe2\x96\xb6 当使用 OpenFileDialog 或 SaveFileDialog 打开或保存文件时应用程序访问的最后一个路径存储在注册表的分支中的HKEY_CURRENT_USER此键下:

\n
Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x96\xb6 应用程序打开或保存的最新文件的名称存储在另一个键(同一注册表分支)下(按文件扩展名组织):

\n
Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\n
Run Code Online (Sandbox Code Playgroud)\n

所有信息都存储为二进制值。
\n第一个键 - LastVisitedPidlMRU- 存储应用程序访问的最后一个路径,有两种可能的形式:

\n
    \n
  • 父 shell 文件夹项,采用经典 GUID 形式(通常代表桌面文件夹),后跟子项,IDlist其中路径以 Unicode 长路径格式和 DOS 短路径格式存储(以及此处未描述的其他小信息 -请参阅进一步阅读链接)。
  • \n
  • 一个以 null 结尾的 Unicode 字符串,表示可执行文件名称,后跟一个IDlist(与之前相同)。
    \n在这里,我将使用此结构来检索指定可执行文件使用的最后一个路径。
  • \n
\n

Key的 Sub-Key 项中包含的 Binary ValuesOpenSavePidlMRU存储为 simple ,因此可以使用SHGetPathFromIDListW()SHGetPathFromIDListEx()IDlist直接访问存储的 Path

\n

在这里,我声明了两者(可能会派上用场),但我只使用第二个,因为它稍微更灵活(好吧,如果需要,它允许检索短路径形式)

\n

进一步阅读或测试:

\n\n
\n

这里有三种方法:

\n

\xe2\x86\x92ApplicationGetLastOpenSavePath用于检索应用程序使用的最后一个路径,给定其可执行文件名称(只是名称,例如app.exe)。
\n由于这个问题已被标记WinForms,您可以将其获取为:

\n
string exeName = Path.GetFileName(Application.ExecutablePath);\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x86\x92ApplicationGetLastOpenSaveFileNames检索应用程序打开的所有文件名,与上次访问的路径相关(仅此而已,因为这是当前问题的内容)。
\n该方法返回一个命名元组,(string AppPath, List<string> FileNames)

\n
    \n
  • AppPath是最后访问的路径。
  • \n
  • FileNamesAppPath是应用程序访问的文件名列表(其中路径所在) 。
  • \n
\n

\xe2\x86\x92 第三个是辅助方法:它的任务是处理对 的调用SHGetPathFromIDListEx(),这需要一些互操作封送。
\n方法参数表示从注册表项中提取的字节数组和一个偏移量,该偏移量表示的初始位置IDlist(如上所述,二进制数据以表示可执行文件名称的以 null 结尾的 Unicode 字符串开始)。

\n
private string ApplicationGetLastOpenSavePath(string executableName)\n{\n    if (string.IsNullOrEmpty(executableName)) return null;\n    string lastVisitedPath = string.Empty;\n    var lastVisitedKey = Registry.CurrentUser.OpenSubKey(\n        @"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU", false);\n\n    string[] values = lastVisitedKey.GetValueNames();\n    foreach (string value in values) {\n        if (value == "MRUListEx") continue;\n        var keyValue = (byte[])lastVisitedKey.GetValue(value);\n\n        string appName = Encoding.Unicode.GetString(keyValue, 0, executableName.Length * 2);\n        if (!appName.Equals(executableName)) continue;\n\n        int offset = executableName.Length * 2 + "\\0\\0".Length;  // clearly null terminated :)\n        lastVisitedPath = GetPathFromIDList(keyValue, offset);\n        break;\n    }\n    return lastVisitedPath;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

第二个方法调用第一个方法来检索由 表示的应用程序访问的最后一个路径string executableName

\n
private (string AppPath, List<string> FileNames) ApplicationGetLastOpenSaveFileNames(string executableName)\n{\n    string appPath = ApplicationGetLastOpenSavePath(executableName);\n    if (string.IsNullOrEmpty(appPath)) return (null, null);\n\n    var fileNames = new List<string>();\n    var extensionMRUKey = Registry.CurrentUser.OpenSubKey(\n        @"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU", false);\n    string[] subKeys = extensionMRUKey.GetSubKeyNames().ToArray();\n\n    foreach (string key in subKeys) {\n        var subKey = extensionMRUKey.OpenSubKey(key);\n\n        foreach (string keyValue in subKey.GetValueNames()) {\n            var value = (byte[])subKey.GetValue(keyValue);\n            string filePath = GetPathFromIDList(value, 0);\n\n            if (filePath.Contains(appPath)) {\n                fileNames.Add(filePath);\n            }\n        }\n    }\n    return (appPath, fileNames);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

帮助程序方法和 Win32 声明:

\n
private string GetPathFromIDList(byte[] idList, int offset)\n{\n    int buffer = 520;  // 520 = MAXPATH * 2\n    var sb = new StringBuilder(buffer);\n\n    IntPtr ptr = Marshal.AllocHGlobal(idList.Length);\n    Marshal.Copy(idList, offset, ptr, idList.Length - offset);\n\n    // or -> bool result = SHGetPathFromIDListW(ptr, sb);\n    bool result = SHGetPathFromIDListEx(ptr, sb, buffer, GPFIDL_FLAGS.GPFIDL_UNCPRINTER);\n    Marshal.FreeHGlobal(ptr);\n    return result ? sb.ToString() : string.Empty;\n}\n\n[DllImport("shell32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]\ninternal static extern bool SHGetPathFromIDListW(\n    IntPtr pidl, \n    [MarshalAs(UnmanagedType.LPTStr)]\n    StringBuilder pszPath);\n\n[DllImport("shell32.dll", CharSet = CharSet.Unicode)]\ninternal static extern bool SHGetPathFromIDListEx(\n    IntPtr pidl, \n    [MarshalAs(UnmanagedType.LPTStr)]\n    [In,Out] StringBuilder pszPath, \n    int cchPath, \n    GPFIDL_FLAGS uOpts);\n\ninternal enum GPFIDL_FLAGS : uint {\n    GPFIDL_DEFAULT = 0x0000,\n    GPFIDL_ALTNAME = 0x0001,\n    GPFIDL_UNCPRINTER = 0x0002\n}\n
Run Code Online (Sandbox Code Playgroud)\n