C++程序如何获得自身路径(Qt源码)

考虑一下:

  • 将 Python 嵌入的到 C++ 中,编译出一个可执行程序
  • 并将 Python 的文件(.py, .so, .pyd,等)放到可执行程序所在目录的某个子目录

那么,嵌入的Python如何找到这些文件呢?

需要知道可执行程序自身路径,可是,C、C++ 标准库没有提供这种东西

只能使用系统api了,而系统api用起来需要注意的问题似乎总是不少,

不过呢,Qt 中提供的这种功能,我们不妨看看它是如何做的:

Qt Manual

QString QCoreApplication::applicationFilePath () [static]

Returns the file path of the application executable.

For example, if you have installed Qt in the /usr/local/qt directory, and you run the regexp example, this function will return "/usr/local/qt/examples/tools/regexp/regexp".

Warning: On Linux, this function will try to get the path from the /proc file system. If that fails, it assumes that argv[0] contains the absolute file name of the executable. The function also assumes that the current directory has not been changed by the application.

恩,这儿已经介绍了 Linux 下是如何实现的了,不过还是看看源码吧,毕竟,

在离开Qt的C++下,需要我们自己写类似的代码。

Unix/Linux

  • 如果是Linux,根据自身的pid,去查找 /proc/<pid>/exe

    • 找到,且是符号连接,那么返回其所指向的路径
  • 根据 命令行参数 argv[0] 进行判断
    • 如果以 “/” ,开头,那么就已经是绝对地址,直接返回
    • 否则 如果 包含 “/”,那么是相对地址,和工作路径合并后返回
    • 否则 遍历系统环境变量 PATH,查找其下和argv[0]同名的文件

源码如下:

#if defined( Q_OS_UNIX )
#  ifdef Q_OS_LINUX
    // Try looking for a /proc/<pid>/exe symlink first which points to
    // the absolute path of the executable
    QFileInfo pfi(QString::fromLatin1("/proc/%1/exe").arg(getpid()));
    if (pfi.exists() && pfi.isSymLink()) {
        d->cachedApplicationFilePath = pfi.canonicalFilePath();
        return d->cachedApplicationFilePath;
    }
#  endif

    QString argv0 = QFile::decodeName(QByteArray(argv()[0]));
    QString absPath;

    if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
        /*
          If argv0 starts with a slash, it is already an absolute
          file path.
        */
        absPath = argv0;
    } else if (argv0.contains(QLatin1Char('/'))) {
        /*
          If argv0 contains one or more slashes, it is a file path
          relative to the current directory.
        */
        absPath = QDir::current().absoluteFilePath(argv0);
    } else {
        /*
          Otherwise, the file path has to be determined using the
          PATH environment variable.
        */
        QByteArray pEnv = qgetenv("PATH");
        QDir currentDir = QDir::current();
        QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1Char(':'));
        for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
            if ((*p).isEmpty())
                continue;
            QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
            QFileInfo candidate_fi(candidate);
            if (candidate_fi.exists() && 

Windows

Windows 提供了 GetModuleFileName 这种函数,所以操作就容易多了,不过Qt源码中也还是一大段哈。

  • 首先,假定路径总长度不超过 MAX_PATH(当前情况下,其值仍然是260,似乎微软也没有动它的打算?)
  • 在 栈 上分配一个 MAX_PATH + 2 长度的数组
  • 如果真实的长度超过了 MAX_PATH,就改用在 堆 中分配内存
    • 分配 2倍MAX_PATH,进行尝试
    • 依然不行,则分配 3倍 MAX_PATH,
    • ...

    // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths
    // up to, and including MAX_PATH size perfectly fine with string termination, as well
    // as easily detect if the file path is indeed larger than MAX_PATH, in which case we
    // need to use the heap instead. This is a work-around, since contrary to what the
    // MSDN documentation states, GetModuleFileName sometimes doesn't set the
    // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if
    // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH.
    // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal
    // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use
    // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok).
    wchar_t buffer[MAX_PATH + 2];
    DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1);
    buffer[MAX_PATH + 1] = 0;

    if (v == 0)
        return QString();
    else if (v <= MAX_PATH)
        return QString::fromWCharArray(buffer);

    // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap
    wchar_t *b = 0;
    int i = 1;
    size_t size;
    do {
        ++i;
        size = MAX_PATH * i;
        b = reinterpret_cast<wchar_t *>(realloc(b, (size + 1) * sizeof(wchar_t)));
        if (b)
            v = GetModuleFileName(NULL, b, size);
    } while (b && v == size);

    if (b)
        *(b + size) = 0;
    QString res = QString::fromWCharArray(b);
    free(b);

你可能感兴趣的:(C++,File,buffer,qt,Path,documentation)