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

来源:互联网 发布:excel重复数据筛选 编辑:程序博客网 时间:2024/05/22 00:26

考虑一下:

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