使用C++代码在Windows原生程序中打开文件

3
我的C++程序创建了一个.png文件,我需要在创建后立即打开(查看)它——有没有一种方法可以像在Windows资源管理器中打开文件一样打开它,以便文件将使用用户首选的程序打开,如果没有与文件格式关联的程序,则Windows将显示对话框窗口,在其中用户将能够选择任何程序来打开该文件。
跨平台(+Linux,+MacOS,+BSD)解决方案将是完美的。
谢谢。

2
你的整个问题都是关于Windows和Windows Explorer的,然后最后一句要求跨平台解决方案。我有什么遗漏吗? - Cody Gray
程序将在Windows和*nix上运行,我编写了许多依赖于平台的代码,使用宏来实现,但是拥有跨平台解决方案会更好。 - Slaus
我猜你已经尝试过system()命令了吧?这是我能想到的最快捷的跨平台方式,但如果你正在问这个问题,我认为它并不能完全满足你的需求。 - Cody Gray
是的,据我所知,system() 需要打开程序的完整路径,而我不知道它。 - Slaus
3个回答

7

4
如果您使用Qt开发跨平台应用程序,那么QDesktopServices ::openUrl()方法就可以完成工作。当然,像Qt中的所有东西一样,它也是跨平台的。
既然您已经在使用wxWidgets,使用Qt只是为了打开一个文件显然有些杀鸡焉用牛刀。而且由于wxWidgets主要是GUI相关的,它可能没有这样的功能,尽管我不能确定,因为我自己从未使用过它。
不过,如果您想以跨平台的方式完成这个操作,这里是Qt在Windows上的做法:
quintptr returnValue = (quintptr)ShellExecute(0, 0,
  (wchar_t*)filePath.utf16(), 0, 0, SW_SHOWNORMAL);
// ShellExecute returns a value greater than 32 if successful
return (returnValue > 32);

在这里,filePath.utf16()是以Unicode编码的以null结尾的文件路径。

以下是X11 / Unix的相关部分:

if (launch(url, QLatin1String("xdg-open")))
    return true;
if (X11->desktopEnvironment == DE_GNOME 
    && launch(url, QLatin1String("gnome-open"))) {
    return true;
} else {
    if (X11->desktopEnvironment == DE_KDE 
        && launch(url, QLatin1String("kfmclient exec")))
        return true;
}
if (launch(url, QLatin1String("firefox")))
    return true;
if (launch(url, QLatin1String("mozilla")))
    return true;
if (launch(url, QLatin1String("netscape")))
    return true;
if (launch(url, QLatin1String("opera")))
    return true;
return false;

在这里,launch()函数基本上启动指定的应用程序,并传递要打开的URL。不仅仅是文件路径,就像在Windows中一样,而是完整的URL,例如file:///home/user/tmp/file.doc。虽然不确定这是否重要。它还会对URL中的所有非ASCII字符进行百分比编码,然后再将其传递给程序。不确定openDocument()尝试的所有程序是否都需要这样做。我已经使用xdg-open进行了测试,它无论是否百分比编码都不会有影响。
这里是检测桌面环境并相应设置X11->desktopEnvironment的部分:
    X11->desktopEnvironment = DE_UNKNOWN;
    Atom type;
    int format;
    unsigned long length, after;
    uchar *data = 0;
    int rc;
    do {
        if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
            X11->desktopEnvironment = DE_KDE;
            break;
        }
        if (qgetenv("DESKTOP_SESSION") == "gnome") {
            X11->desktopEnvironment = DE_GNOME;
            break;
        }
        // GNOME_DESKTOP_SESSION_ID is deprecated for some reason, but still check it
        if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
            X11->desktopEnvironment = DE_GNOME;
            break;
        }
        rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(_DT_SAVE_MODE),
                                0, 2, False, XA_STRING, &type, &format, &length,
                                &after, &data);
        if (rc == Success && length) {
            if (!strcmp(reinterpret_cast<char *>(data), "xfce4")) {
                // Pretend that xfce4 is gnome, as it uses the same libraries.
                // The detection above is stolen from xdg-open.
                X11->desktopEnvironment = DE_GNOME;
                break;
            }
            // We got the property but it wasn't xfce4. Free data before it gets overwritten.
            XFree(data);
            data = 0;
        }
    } while(0);

哇,那真是一件了不起的事情。我已经移除了检测其他环境的部分,因为它们在 openDocument() 中没有用到。

最后,这里是光荣的 Mac 版本 openDocument():

// LSOpen does not work in this case, use QProcess open instead.
return QProcess::startDetached(QLatin1String("open"), QStringList() << file.toLocalFile());

真的吗?就这样了?哇,Mac平台肯定有些东西。在这里,QProcess :: startDetached()只是启动一个新进程,并将文件路径作为参数传递。它在很大程度上等同于system()调用,但不等待进程终止。不确定是否重要,并且我不知道如何在Mac上使用QProcess之外完成它,因为我从未见过Mac。


1
为什么要假设提问者正在使用Qt? - Cody Gray
@Cody,我并没有假设那个。然而,如果不使用跨平台库来进行跨平台操作,几乎是不可能的,因此使用一个跨平台库是一个好主意。我只是选择了我最熟悉的那个。 - Sergei Tachenov
Qt的许可证过于复杂,因此我更喜欢wxWidgets。 - Slaus
@Slav,不过你可能希望查看Qt源代码,以找出它们如何以跨平台的方式实现它。 - Sergei Tachenov
哦,那是个好主意!有没有Qt在线资源的链接和互联网链接? - Slaus
显示剩余3条评论

2
这是一个打开应用程序中位图的示例:
ShellExecute(   GetSafeHwnd(),
                      _T("open"),
                      "Test.bmp",
                      NULL,
                      NULL,
                      SW_SHOW);

如果您需要跨平台版本,可以在谷歌上搜索相关信息。

再见


1
请注意,GetSafeHwnd 是 MFC 函数,不是标准 Windows API 的一部分。您只需要指定一个窗口句柄 (HWND) 即可。 - Cody Gray

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