在我看来,Linux使用/proc/self/exe很容易但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。我见过一些项目胡乱摆弄argv[0],但它似乎并不完全可靠。
如果你必须支持,比如说,Mac OS X,它没有/proc/,你会怎么做?使用#ifdefs来隔离平台特定的代码(例如NSBundle)?或者尝试从argv[0], $ path和诸如此类的东西中推断可执行文件的路径,冒着在边缘情况下发现错误的风险?
在我看来,Linux使用/proc/self/exe很容易但是我想知道是否有一种方便的方法来找到当前应用程序的目录在C/ c++与跨平台接口。我见过一些项目胡乱摆弄argv[0],但它似乎并不完全可靠。
如果你必须支持,比如说,Mac OS X,它没有/proc/,你会怎么做?使用#ifdefs来隔离平台特定的代码(例如NSBundle)?或者尝试从argv[0], $ path和诸如此类的东西中推断可执行文件的路径,冒着在边缘情况下发现错误的风险?
当前回答
这只是我的个人意见。通过使用这段代码,您可以在具有跨平台接口的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
}
你可以在这里详细看看。
其他回答
在Linux上使用/proc/self/exe或argv[0]的另一种选择是使用ELF解释器传递的信息,由glibc提供:
#include <stdio.h>
#include <sys/auxv.h>
int main(int argc, char **argv)
{
printf("%s\n", (char *)getauxval(AT_EXECFN));
return(0);
}
请注意,getauxval是一个glibc扩展,为了健壮起见,您应该检查它是否返回NULL(表明ELF解释器没有提供AT_EXECFN参数),但我认为这在Linux上实际上从来都不是问题。
一些特定于操作系统的接口:
Mac OS X: _NSGetExecutablePath() (man 3 dyld) Linux: readlink /proc/self/exe Solaris: getexecname () sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1 readlink /proc/curproc/file (FreeBSD默认没有procfs) NetBSD: readlink /proc/curproc/exe DragonFly BSD: readlink /proc/curproc/file Windows: GetModuleFileName() with hModule = NULL
也有第三方库可以用来获取这些信息,比如在prideout的回答中提到的whereami,或者如果你使用Qt,评论中提到的QCoreApplication::applicationFilePath()。
可移植的(但不太可靠)方法是使用argv[0]。尽管调用程序可以将它设置为任何东西,但按照惯例,它要么被设置为可执行文件的路径名,要么被设置为使用$ path找到的名称。
一些shell,包括bash和ksh,在执行可执行文件之前将环境变量“_”设置为可执行文件的完整路径。在这种情况下,您可以使用getenv("_")来获取它。然而,这是不可靠的,因为不是所有的shell都这样做,并且它可以被设置为任何东西,或者是在执行程序之前没有更改它的父进程遗留下来的。
依我看,没有这种办法。还有一个不明确的地方:如果同一个可执行文件有多个硬链接指向它,您希望得到什么作为答案?(硬链接实际上并不“指向”,它们是同一个文件,只是位于文件系统层次结构中的另一个位置。)
一旦execve()成功执行了一个新的二进制文件,所有关于原始程序参数的信息都将丢失。
您可以使用argv[0]并分析PATH环境变量。 看:一个可以找到自己的程序示例
Gregory Pakosz的whereami库使用mark4o文章中提到的api为各种平台实现了这一点。如果您“只是”需要一个适用于可移植项目的解决方案,而对各种平台的特性不感兴趣,那么这是最有趣的。
在撰写本文时,受支持的平台有:
窗户 Linux Mac iOS 安卓 QNX Neutrino FreeBSD NetBSD 蜻蜓BSD SunOS
该库由whereami.c和whereami.h组成,并在MIT和WTFPL2下获得许可。将文件放入您的项目中,包括头文件并使用它:
#include "whereami.h"
int main() {
int length = wai_getExecutablePath(NULL, 0, NULL);
char* path = (char*)malloc(length + 1);
wai_getExecutablePath(path, length, &dirname_length);
path[length] = '\0';
printf("My path: %s", path);
free(path);
return 0;
}