如何打开资源管理器并选择特定文件?

83

我想编写一个函数,你可以通过传递一个文件路径来使用它,例如:

C:\FOLDER\SUBFOLDER\FILE.TXT

我希望打开Windows资源管理器并定位到包含该文件的文件夹,然后在该文件夹中选择该文件(类似于许多程序中使用的“在文件夹中显示”概念)。

我应该如何做?


1
还有,是否可以选择多个文件? - Colonel Panic
可能是实现“打开包含文件夹”并突出显示文件的重复问题。 - RandomEngy
3个回答

177

不使用Win32 shell函数的最简单方法是使用/select参数启动explorer.exe。例如,启动进程

explorer.exe /select,"C:\Folder\subfolder\file.txt"

将打开一个新的资源管理器窗口到C:\Folder\subfolder,并选择file.txt文件。

如果您希望以编程方式而不启动新进程执行此操作,则需要使用shell函数SHOpenFolderAndSelectItems,这是explorer.exe内部使用的/select命令。请注意,这需要使用PIDL,并且如果您不熟悉shell API的工作原理,则可能会很麻烦。

以下是基于/select方法的完整程序化实现,其中路径清理感谢@Bhushan和@tehDorf的建议:

public bool ExploreFile(string filePath) {
    if (!System.IO.File.Exists(filePath)) {
        return false;
    }
    //Clean up file path so it can be navigated OK
    filePath = System.IO.Path.GetFullPath(filePath);
    System.Diagnostics.Process.Start("explorer.exe", string.Format("/select,\"{0}\"", filePath));
    return true;
}

参考资料: Explorer.exe 命令行开关


7
您需要在select后添加逗号,像这样:explorer.exe /select, "c:\folder\subfolder\file.txt"。 - Justin Harvey
启动新进程有哪些缺点? - Gusdor
@Gusdor 没有什么特别的。除非您在文件夹视图选项中启用了在单独进程中启动文件夹窗口(默认情况下禁用),否则这实际上不会启动新进程。它只是模拟,而现有的 explorer.exe 实例将被调用来处理请求,而不是在单独的内存空间中启动新进程。如果您经常调用此代码,则在打开序列期间使用 SHOpenFolderAndSelectItems 肯定更有效率和节约资源。 - Mahmoud Al-Qudsi
2
@MahmoudAl-Qudsi 你可以使用 filePath = System.IO.Path.GetFullPath(filePath); 来清理文件路径,而不是使用 Regex。它将规范化目录分隔符,并解析相对路径,例如如果您传递类似于 @"..\some\relative\file\path.txt"@"C:\Some\relative\..\file\path.txt" 的内容。您还可以更新示例以使用新的字符串插值:Process.Start("explorer.exe", $"/select,\"{filePath}\""); - tehDorf
1
如果文件名本身包含逗号,则似乎无法正常工作。 - Joan Charmant
启动资源管理器的缺点是它比SHOpenFolderAndSelectItems慢,并且往往会在后台打开资源管理器。因此,我推荐使用SHOpenFolderAndSelectItems - undefined

10
自Windows XP起(即不支持Windows 2000或更早版本),SHOpenFolderAndSelectItems是一种被支持的方法:
void OpenFolderAndSelectItem(String filename)
{
   // Parse the full filename into a pidl
   PIDLIST_ABSOLUTE pidl;
   SFGAO flags;
   SHParseDisplayName(filename, null, out pidl, 0, out flags);
   try 
   {
      // Open Explorer and select the thing
      SHOpenFolderAndSelectItems(pidl, 0, null, 0);
   }
   finally 
   {
      // Use the task allocator to free to returned pidl
      CoTaskMemFree(pidl);
   }
}

1

为了跟进@Mahmoud Al-Qudsi的回答。当他说“启动进程”时,这是对我有用的方法:

// assume variable "path" has the full path to the file, but possibly with / delimiters
for ( int i = 0 ; path[ i ] != 0 ; i++ )
{
    if ( path[ i ] == '/' )
    {
        path[ i ] = '\\';
    }
}
std::string s = "explorer.exe /select,\"";
s += path;
s += "\"";
PROCESS_INFORMATION processInformation;
STARTUPINFOA startupInfo;
ZeroMemory( &startupInfo, sizeof(startupInfo) );
startupInfo.cb = sizeof( STARTUPINFOA );
ZeroMemory( &processInformation, sizeof( processInformation ) );
CreateProcessA( NULL, (LPSTR)s.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation );

1
这个问题标记为C#,但这是一个C++的答案,可能是正确的,但对于寻找C#/.NET解决方案的人来说并不相关。 - jv42
我点赞了,因为这种方法在C#中同样行之有效,只需使用Process.Start("explorer.exe " + "/select, " + '"'+ filePath.Replace('/', '\') + '"'); 将前斜杠替换为反斜杠即可。 - Ivan Silkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接