QDesktopServices::openUrl() 如果参数是文件夹会在资源管理器打开,如果参数是文件会用默认打开方式打开,不符合需求。想要打开文件夹并选中文件,在 Windows 上可以用 QProcess 执行 explorer 命令:
QString command = "explorer.exe";
QStringList arguments;
const QFileInfo file_info(file_path);
if (!file_info.isDir()) {
arguments << QLatin1String("/select,");
}
arguments << QDir::toNativeSeparators(file_info.canonicalFilePath());
QProcess::startDetached(command, arguments);
但是现在有两个问题:
1.第一次执行可能只会打开文件夹,再次执行才会选中文件(测试环境 Win10);
2.想要支持 Mac 系统。
这时候就需要参考下其他的开源代码,比如 Qt Creator 5 的源码:
//qt-creator-opensource-src-5.0.0\src\plugins\coreplugin\fileutils.cpp
void FileUtils::showInGraphicalShell(QWidget *parent, const QString &pathIn)
{
const QFileInfo fileInfo(pathIn);
// Mac, Windows support folder or file.
if (HostOsInfo::isWindowsHost()) {
const FilePath explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
QApplication::translate("Core::Internal",
"Launching Windows Explorer Failed"),
QApplication::translate("Core::Internal",
"Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QStringList param;
if (!fileInfo.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
QProcess::startDetached(explorer.toString(), param);
} else if (HostOsInfo::isMacHost()) {
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(fileInfo.canonicalFilePath());
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e")
<< QLatin1String("tell application \"Finder\" to activate");
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
} else {
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
const QString app = UnixUtils::fileBrowser(ICore::settings());
QStringList browserArgs = ProcessArgs::splitArgs(
UnixUtils::substituteFileBrowserParameters(app, folder));
QString error;
if (browserArgs.isEmpty()) {
error = QApplication::translate("Core::Internal",
"The command for file browser is not set.");
} else {
QProcess browserProc;
browserProc.setProgram(browserArgs.takeFirst());
browserProc.setArguments(browserArgs);
const bool success = browserProc.startDetached();
error = QString::fromLocal8Bit(browserProc.readAllStandardError());
if (!success && error.isEmpty())
error = QApplication::translate("Core::Internal",
"Error while starting file browser.");
}
if (!error.isEmpty())
showGraphicalShellError(parent, app, error);
}
}
在 Qt Creator 9 中 Mac 下的命令变了:
void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn)
{
// ... ...
} else if (HostOsInfo::isMacHost()) {
QtcProcess::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
} else {
// ... ...
}
bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid)
{
return QProcess::startDetached(cmd.executable().toUserOutput(),
cmd.splitArguments(),
workingDirectory.toUserOutput(),
pid);
}
很显然,他在 Windows 上也是用的 explorer,所以第一次打开也会有问题。其次,Qt Creator 虽然支持 Mac,但是 Qt Creator 9 右键打开文件位置有时候非常慢(测试环境 Mac 10.14)。
然后我又找到 Wireshark 的源码也有这个功能,和 Qt Creator 5 差不多,使用 osascript 在 Mac 上打开 Finder 速度有时候也很慢。具体哪个版本的代码合适还需要进行更多的测试。
// https://github.com/wireshark/wireshark
void desktop_show_in_folder(const QString &file_path)
{
bool success = false;
// https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
#if defined(Q_OS_WIN)
QString command = "explorer.exe";
QStringList arguments;
QString path = QDir::toNativeSeparators(file_path);
arguments << "/select," << path + "";
success = QProcess::startDetached(command, arguments);
#elif defined(Q_OS_MAC)
QStringList script_args;
QString escaped_path = file_path;
escaped_path.replace('"', "\\\"");
script_args << "-e"
<< QString("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(escaped_path);
if (QProcess::execute("/usr/bin/osascript", script_args) == 0) {
success = true;
script_args.clear();
script_args << "-e"
<< "tell application \"Finder\" to activate";
QProcess::execute("/usr/bin/osascript", script_args);
}
#else
// Is there a way to highlight the file using xdg-open?
#endif
if (!success) {
QFileInfo file_info(file_path);
QDesktopServices::openUrl(QUrl::fromLocalFile(file_info.dir().absolutePath()));
}
}
解决了 Mac 版的功能,回头又去找 Windows 打开文件夹并选中文件的的方案,发现 COM 接口的效果还不错,始终能打开并选中:
#include <Windows.h>
#include <objbase.h>
#pragma comment(lib, "Ole32.lib")
#include <ShlObj_core.h>
#pragma comment(lib, "Shell32.lib")
void win_show_in_folder(const QString &file_path)
{
::CoInitialize(nullptr);
QString path = QDir::toNativeSeparators(file_path);
std::wstring str = path.toStdWString();
PIDLIST_ABSOLUTE pidl = ::ILCreateFromPathW((PCWSTR)str.c_str());
if(pidl){
::SHOpenFolderAndSelectItems(pidl,0,0,0);
::ILFree(pidl);
}
::CoUninitialize();
}