3

My C++ program creates .png file and I need to open (view) this file right after creation - is there a way to open it as so it was opened within Windows explorer - so file wiil be opened with user's preferred program and if there are no associated programs with file's format - Windows will show dialog window in which user will be able to select any program to open that file.

Crossplatform (+Linux, +MacOS, +BSD) solution will be perfect.

Thanks.

Slaus
  • 2,086
  • 4
  • 26
  • 41
  • 2
    Your entire question is about Windows and Windows Explorer, then the last sentence requests a cross-platform solution. Am I missing something? – Cody Gray - on strike Feb 26 '11 at 13:49
  • Program will run both on Windows and *nix and I write much of platform-dependent code with macros but it will be better to have cross-platform solution. – Slaus Feb 26 '11 at 13:52
  • I suppose you've tried the `system()` command? That's the quickest way I can think of to go cross-platform, but I assume it doesn't do exactly what you want, if you're asking this question. – Cody Gray - on strike Feb 26 '11 at 14:02
  • Yes, as I know, system() requires opening program's full path which I do not know. – Slaus Feb 26 '11 at 14:07

3 Answers3

7

On Windows, you can use the ShellExecute function.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Andrey
  • 59,039
  • 12
  • 119
  • 163
4

If you used Qt to develop your cross-platform application, then the QDesktopServices::openUrl() method would do the job. It is of course cross-platform, like everything in Qt.

Since you're already using wxWidgets, using Qt just to open a file is obviously an overkill. And since wxWidgets is mostly GUI stuff, it probably doesn't have anything like that, although I can't be sure as I've never used it myself.

Still, if you want to do it in a cross-platform way, here is what Qt does for 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);

Here, filePath.utf16() is the Unicode null-terminated file path.

Here is the relevant part for 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;

Here, the launch() function basically starts the indicated application, passing it the URL to open. Not just the file path, like in Windows, but the complete URL like file:///home/user/tmp/file.doc. Not sure that it matters, though. It also percent-encodes all non-ASCII characters in the URL before passing it to the program. Not sure if it matters for all the programs that the openDocument() tries. I have tested it with xdg-open, and it doesn't care if it's percent-encoded or not.

Here is the part that detects desktop environment and sets X11->desktopEnvironment accordingly:

    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);

Wow, that was something. And I have removed the parts that detects other environments because they aren't used in openDocument().

And lastly, here is the glorious Mac version of the openDocument():

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

Really? That's it? Wow, there must be something to the Mac platform after all. Here, QProcess::startDetached() just starts a new process, passing the file path as an argument. It is largely equivalent to the system() call, but doesn't wait for the process to terminate. Not sure if that matters, and I have no idea how to do it on Mac without using QProcess, as I've never even seen a Mac.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • 1
    Why assume that the asker is using Qt? – Cody Gray - on strike Feb 26 '11 at 13:58
  • @Cody, I wasn't assuming that. However, doing things in a cross platform way without using a cross-platform library is nearly impossible, so using one is a good idea anyway. I just picked up the one I am most familiar with. – Sergei Tachenov Feb 26 '11 at 14:09
  • Qt has too tangled licence so I preferred wxWidgets. – Slaus Feb 26 '11 at 14:09
  • @Slav, I see. Then using Qt just to open a file is probably not a good idea. Qt was LGPLed not so long ago, by the way, just in case you want to use it someday. – Sergei Tachenov Feb 26 '11 at 14:16
  • @Slav, still, you may wish to look at the Qt sources to figure out how they do it in a cross-platform way. – Sergei Tachenov Feb 26 '11 at 14:17
  • Oh, that's the good idea! Is there some link to Qt's online sources with interlinks? – Slaus Feb 26 '11 at 14:19
  • @Slav, I wish there was! Every time I had to find something there I have to dig in the unpacked tarball, which is both slow and annoying, although not that hard. If you find something, let me know. If I figure out something from the sources, I'll update my answer with more information. – Sergei Tachenov Feb 26 '11 at 14:25
  • Heh, pretty big stuff, isn't it? May be it's better to switch from wxWidgets from Qt... – Slaus Feb 28 '11 at 10:14
2

Here is an example of opening a bitmap from an application:

ShellExecute(   GetSafeHwnd(),
                      _T("open"),
                      "Test.bmp",
                      NULL,
                      NULL,
                      SW_SHOW);

For the cross-platform version if you google your request you'll find a lot of information.

Bye

gionny
  • 247
  • 2
  • 10