在我看来,Linux使用/proc/self/exe很容易但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。我见过一些项目胡乱摆弄argv[0],但它似乎并不完全可靠。

如果你必须支持,比如说,Mac OS X,它没有/proc/,你会怎么做?使用#ifdefs来隔离平台特定的代码(例如NSBundle)?或者尝试从argv[0], $ path和诸如此类的东西中推断可执行文件的路径,冒着在边缘情况下发现错误的风险?


当前回答

当然,这并不适用于所有项目。 尽管如此,QCoreApplication::applicationFilePath()在6年的c++ /Qt开发中从未让我失望过。

当然,在尝试使用它之前,应该彻底阅读文档:

警告:在Linux上,此函数将尝试从 /proc文件系统。如果失败,则假定argv[0]包含 可执行文件的绝对文件名。这个函数也假设 应用程序没有更改当前目录。

老实说,我认为#ifdef和其他类似的解决方案根本不应该在现代代码中使用。

我相信更小的跨平台库也存在。让他们把所有特定于平台的东西封装在里面。

其他回答

依我看,没有这种办法。还有一个不明确的地方:如果同一个可执行文件有多个硬链接指向它,您希望得到什么作为答案?(硬链接实际上并不“指向”,它们是同一个文件,只是位于文件系统层次结构中的另一个位置。)

一旦execve()成功执行了一个新的二进制文件,所有关于原始程序参数的信息都将丢失。

如果你曾经支持过,比如说,麦克 OS X没有/proc/ 你会这么做吗?使用#ifdefs来 隔离特定于平台的代码 (例如NSBundle)?

是的,使用#ifdefs隔离特定于平台的代码是实现这一点的常规方法。

另一种方法是有一个干净的#ifdef-less头,其中包含函数声明,并将实现放在特定于平台的源文件中。

例如,查看POCO(可移植组件)c++库如何为它们的Environment类做类似的事情。

但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。

你不能这样做(至少在Linux上)

因为一个可执行文件可以,在运行它的进程执行期间,重命名(2)它的文件路径到不同的目录(同一文件系统)。请参见syscalls(2)和inode(7)。

在Linux上,一个可执行文件(原则上)甚至可以通过调用unlink(2)来删除(3)本身。Linux内核应该继续分配该文件,直到没有进程再引用它为止。使用proc(5),你可以做一些奇怪的事情(例如重命名(2)/proc/self/exe文件,等等…)

换句话说,在Linux上,“当前应用程序的目录”的概念没有任何意义。

请参阅高级Linux编程和操作系统:三个简单的部分。

还可以在OSDEV上查看一些开源操作系统(包括FreeBSD或GNU Hurd)。其中一些提供了接近POSIX的接口(API)。

考虑使用(在获得许可的情况下)跨平台c++框架,如Qt或POCO,也许可以通过将它们移植到您喜欢的操作系统来对它们做出贡献。

要使其可靠地跨平台工作,需要使用#ifdef语句。

下面的代码可以在Windows、Linux、MacOS、Solaris或FreeBSD(尽管FreeBSD未经测试)中找到可执行文件的路径。它使用 Boost 1.55.0(或更高版本)来简化代码,但如果您愿意,可以很容易地删除它。只要根据操作系统和编译器的要求使用像_MSC_VER和__linux这样的定义即可。

#include <string>
#include <boost/predef/os.h>

#if (BOOST_OS_WINDOWS)
#  include <stdlib.h>
#elif (BOOST_OS_SOLARIS)
#  include <stdlib.h>
#  include <limits.h>
#elif (BOOST_OS_LINUX)
#  include <unistd.h>
#  include <limits.h>
#elif (BOOST_OS_MACOS)
#  include <mach-o/dyld.h>
#elif (BOOST_OS_BSD_FREE)
#  include <sys/types.h>
#  include <sys/sysctl.h>
#endif

/*
 * Returns the full path to the currently running executable,
 * or an empty string in case of failure.
 */
std::string getExecutablePath() {
    #if (BOOST_OS_WINDOWS)
        char *exePath;
        if (_get_pgmptr(&exePath) != 0)
            exePath = "";
    #elif (BOOST_OS_SOLARIS)
        char exePath[PATH_MAX];
        if (realpath(getexecname(), exePath) == NULL)
            exePath[0] = '\0';
    #elif (BOOST_OS_LINUX)
        char exePath[PATH_MAX];
        ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath));
        if (len == -1 || len == sizeof(exePath))
            len = 0;
        exePath[len] = '\0';
    #elif (BOOST_OS_MACOS)
        char exePath[PATH_MAX];
        uint32_t len = sizeof(exePath);
        if (_NSGetExecutablePath(exePath, &len) != 0) {
            exePath[0] = '\0'; // buffer too small (!)
        } else {
            // resolve symlinks, ., .. if possible
            char *canonicalPath = realpath(exePath, NULL);
            if (canonicalPath != NULL) {
                strncpy(exePath,canonicalPath,len);
                free(canonicalPath);
            }
        }
    #elif (BOOST_OS_BSD_FREE)
        char exePath[2048];
        int mib[4];  mib[0] = CTL_KERN;  mib[1] = KERN_PROC;  mib[2] = KERN_PROC_PATHNAME;  mib[3] = -1;
        size_t len = sizeof(exePath);
        if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0)
            exePath[0] = '\0';
    #endif
        return std::string(exePath);
}

上面的版本返回包括可执行文件名称在内的完整路径。如果相反,你想要没有可执行名称的路径,#include boost/ filessystem .hpp>,并将返回语句更改为:

return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();

这只是我的个人意见。通过使用这段代码,您可以在具有跨平台接口的C/ c++中找到当前应用程序的目录。

void getExecutablePath(char ** path, unsigned int * pathLength)
{
    // Early exit when invalid out-parameters are passed
    if (!checkStringOutParameter(path, pathLength))
    {
        return;
    }

#if defined SYSTEM_LINUX

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Return written bytes, indicating if memory was sufficient
    int len = readlink("/proc/self/exe", exePath, PATH_MAX);

    if (len <= 0 || len == PATH_MAX) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_WINDOWS

    // Preallocate MAX_PATH (e.g., 4095) characters and hope the executable path isn't longer (including null byte)
    char exePath[MAX_PATH];

    // Return written bytes, indicating if memory was sufficient
    unsigned int len = GetModuleFileNameA(GetModuleHandleA(0x0), exePath, MAX_PATH);
    if (len == 0) // memory not sufficient or general error occured
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_SOLARIS

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    // Convert executable path to canonical path, return null pointer on error
    if (realpath(getexecname(), exePath) == 0x0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    unsigned int len = strlen(exePath);
    copyToStringOutParameter(exePath, len, path, pathLength);

#elif defined SYSTEM_DARWIN

    // Preallocate PATH_MAX (e.g., 4096) characters and hope the executable path isn't longer (including null byte)
    char exePath[PATH_MAX];

    unsigned int len = (unsigned int)PATH_MAX;

    // Obtain executable path to canonical path, return zero on success
    if (_NSGetExecutablePath(exePath, &len) == 0)
    {
        // Convert executable path to canonical path, return null pointer on error
        char * realPath = realpath(exePath, 0x0);

        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }
    else // len is initialized with the required number of bytes (including zero byte)
    {
        char * intermediatePath = (char *)malloc(sizeof(char) * len);

        // Convert executable path to canonical path, return null pointer on error
        if (_NSGetExecutablePath(intermediatePath, &len) != 0)
        {
            free(intermediatePath);
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        char * realPath = realpath(intermediatePath, 0x0);

        free(intermediatePath);

        // Check if conversion to canonical path succeeded
        if (realPath == 0x0)
        {
            invalidateStringOutParameter(path, pathLength);
            return;
        }

        // Copy contents to caller, create caller ownership
        unsigned int len = strlen(realPath);
        copyToStringOutParameter(realPath, len, path, pathLength);

        free(realPath);
    }

#elif defined SYSTEM_FREEBSD

    // Preallocate characters and hope the executable path isn't longer (including null byte)
    char exePath[2048];

    unsigned int len = 2048;

    int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };

    // Obtain executable path by syscall
    if (sysctl(mib, 4, exePath, &len, 0x0, 0) != 0)
    {
        invalidateStringOutParameter(path, pathLength);
        return;
    }

    // Copy contents to caller, create caller ownership
    copyToStringOutParameter(exePath, len, path, pathLength);

#else

    // If no OS could be detected ... degrade gracefully
    invalidateStringOutParameter(path, pathLength);

#endif
}

你可以在这里详细看看。