获取可执行文件的path

我知道这个问题之前已经被问过,但是我还没有看到一个满意的答案,或者是一个明确的“不,这个不能做”,所以我会再问一次!

我所要做的就是以独立于平台的方式获取当前正在运行的可执行文件的path,无论是作为绝对path还是相对于可执行文件的调用位置。 我虽然boost :: filesystem :: initial_path是我的麻烦的答案,但似乎只处理问题的“平台无关”的一部分 – 它仍然返回的应用程序被调用的path。

对于一些背景,这是一个使用Ogre的游戏,我试图使用Very Sleepy进行configuration,它从自己的目录运行目标可执行文件,所以当然在加载游戏时发现没有configuration文件等,并及时崩溃。 我希望能够将它传递给configuration文件的绝对path,我知道它将永远与可执行文件一起生存。 在Visual Studio中debugging同样如此 – 我想能够运行$(TargetPath)而不必设置工作目录。

我知道没有跨平台的方式。

对于Linux:readlink / proc / self / exe

Windows:GetModuleFileName

这种方式使用boost + argv。 您提到这可能不是跨平台,因为它可能包含或不包含可执行文件的名称。 那么下面的代码应该解决这个问题。

#include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> #include <iostream> namespace fs = boost::filesystem; int main(int argc,char** argv) { fs::path full_path( fs::initial_path<fs::path>() ); full_path = fs::system_complete( fs::path( argv[0] ) ); std::cout << full_path << std::endl; //Without file name std::cout << full_path.stem() << std::endl; //std::cout << fs::basename(full_path) << std::endl; return 0; } 

下面的代码获取当前的工作目录,可以做你需要的

 #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> #include <iostream> namespace fs = boost::filesystem; int main(int argc,char** argv) { //current working directory fs::path full_path( fs::current_path<fs::path>() ); std::cout << full_path << std::endl; std::cout << full_path.stem() << std::endl; //std::cout << fs::basepath(full_path) << std::endl; return 0; } 

注意刚刚意识到basename( )已被弃用,所以不得不切换到.stem()

以下是我的解决scheme。 我已经在Windows,Mac OS X,Solaris,BSD和GNU / Linux上进行了testing。

SRC / executable_path.cpp

 #include <cstdio> #include <cstdlib> #include <algorithm> #include <iterator> #include <string> #include <vector> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> #include <boost/predef.h> #include <boost/version.hpp> #include <boost/tokenizer.hpp> #if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0)) # include <boost/process.hpp> #endif #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) # include <Windows.h> #endif #include <boost/executable_path.hpp> #include <boost/detail/executable_path_internals.hpp> namespace boost { #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) std::string executable_path(const char* argv0) { typedef std::vector<char> char_vector; typedef std::vector<char>::size_type size_type; char_vector buf(1024, 0); size_type size = buf.size(); bool havePath = false; bool shouldContinue = true; do { DWORD result = GetModuleFileNameA(nullptr, &buf[0], size); DWORD lastError = GetLastError(); if (result == 0) { shouldContinue = false; } else if (result < size) { havePath = true; shouldContinue = false; } else if ( result == size && (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS) ) { size *= 2; buf.resize(size); } else { shouldContinue = false; } } while (shouldContinue); if (!havePath) { return detail::executable_path_fallback(argv0); } // On Microsoft Windows, there is no need to call boost::filesystem::canonical or // boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA // is the one we want. std::string ret = &buf[0]; return ret; } #elif (BOOST_OS_MACOS) # include <mach-o/dyld.h> std::string executable_path(const char* argv0) { typedef std::vector<char> char_vector; char_vector buf(1024, 0); uint32_t size = static_cast<uint32_t>(buf.size()); bool havePath = false; bool shouldContinue = true; do { int result = _NSGetExecutablePath(&buf[0], &size); if (result == -1) { buf.resize(size + 1); std::fill(std::begin(buf), std::end(buf), 0); } else { shouldContinue = false; if (buf.at(0) != 0) { havePath = true; } } } while (shouldContinue); if (!havePath) { return detail::executable_path_fallback(argv0); } std::string path(&buf[0], size); boost::system::error_code ec; boost::filesystem::path p( boost::filesystem::canonical(path, boost::filesystem::current_path(), ec)); if (ec.value() == boost::system::errc::success) { return p.make_preferred().string(); } return detail::executable_path_fallback(argv0); } #elif (BOOST_OS_SOLARIS) # include <stdlib.h> std::string executable_path(const char* argv0) { std::string ret = getexecname(); if (ret.empty()) { return detail::executable_path_fallback(argv0); } boost::filesystem::path p(ret); if (!p.has_root_directory()) { boost::system::error_code ec; p = boost::filesystem::canonical( p, boost::filesystem::current_path(), ec); if (ec.value() != boost::system::errc::success) { return detail::executable_path_fallback(argv0); } ret = p.make_preferred().string(); } return ret; } #elif (BOOST_OS_BSD) # include <sys/sysctl.h> std::string executable_path(const char* argv0) { typedef std::vector<char> char_vector; int mib[4]{0}; size_t size; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; int result = sysctl(mib, 4, nullptr, &size, nullptr, 0); if (-1 == result) { return detail::executable_path_fallback(argv0); } char_vector buf(size + 1, 0); result = sysctl(mib, 4, &buf[0], &size, nullptr, 0); if (-1 == result) { return detail::executable_path_fallback(argv0); } std::string path(&buf[0], size); boost::system::error_code ec; boost::filesystem::path p( boost::filesystem::canonical( path, boost::filesystem::current_path(), ec)); if (ec.value() == boost::system::errc::success) { return p.make_preferred().string(); } return detail::executable_path_fallback(argv0); } #elif (BOOST_OS_LINUX) # include <unistd.h> std::string executable_path(const char *argv0) { typedef std::vector<char> char_vector; typedef std::vector<char>::size_type size_type; char_vector buf(1024, 0); size_type size = buf.size(); bool havePath = false; bool shouldContinue = true; do { ssize_t result = readlink("/proc/self/exe", &buf[0], size); if (result < 0) { shouldContinue = false; } else if (static_cast<size_type>(result) < size) { havePath = true; shouldContinue = false; size = result; } else { size *= 2; buf.resize(size); std::fill(std::begin(buf), std::end(buf), 0); } } while (shouldContinue); if (!havePath) { return detail::executable_path_fallback(argv0); } std::string path(&buf[0], size); boost::system::error_code ec; boost::filesystem::path p( boost::filesystem::canonical( path, boost::filesystem::current_path(), ec)); if (ec.value() == boost::system::errc::success) { return p.make_preferred().string(); } return detail::executable_path_fallback(argv0); } #else std::string executable_path(const char *argv0) { return detail::executable_path_fallback(argv0); } #endif } 

SRC /细节/ executable_path_internals.cpp

 #include <cstdio> #include <cstdlib> #include <algorithm> #include <iterator> #include <string> #include <vector> #include <boost/filesystem/operations.hpp> #include <boost/filesystem/path.hpp> #include <boost/predef.h> #include <boost/version.hpp> #include <boost/tokenizer.hpp> #if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0)) # include <boost/process.hpp> #endif #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) # include <Windows.h> #endif #include <boost/executable_path.hpp> #include <boost/detail/executable_path_internals.hpp> namespace boost { namespace detail { std::string GetEnv(const std::string& varName) { if (varName.empty()) return ""; #if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS) char* value = std::getenv(varName.c_str()); if (!value) return ""; return value; #elif (BOOST_OS_WINDOWS) typedef std::vector<char> char_vector; typedef std::vector<char>::size_type size_type; char_vector value(8192, 0); size_type size = value.size(); bool haveValue = false; bool shouldContinue = true; do { DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size); if (result == 0) { shouldContinue = false; } else if (result < size) { haveValue = true; shouldContinue = false; } else { size *= 2; value.resize(size); } } while (shouldContinue); std::string ret; if (haveValue) { ret = &value[0]; } return ret; #else return ""; #endif } bool GetDirectoryListFromDelimitedString( const std::string& str, std::vector<std::string>& dirs) { typedef boost::char_separator<char> char_separator_type; typedef boost::tokenizer< boost::char_separator<char>, std::string::const_iterator, std::string> tokenizer_type; dirs.clear(); if (str.empty()) { return false; } #if (BOOST_OS_WINDOWS) const std::string os_pathsep(";"); #else const std::string os_pathsep(":"); #endif char_separator_type pathSep(os_pathsep.c_str()); tokenizer_type strTok(str, pathSep); typename tokenizer_type::iterator strIt; typename tokenizer_type::iterator strEndIt = strTok.end(); for (strIt = strTok.begin(); strIt != strEndIt; ++strIt) { dirs.push_back(*strIt); } if (dirs.empty()) { return false; } return true; } std::string search_path(const std::string& file) { if (file.empty()) return ""; std::string ret; #if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0)) { namespace bp = boost::process; boost::filesystem::path p = bp::search_path(file); ret = p.make_preferred().string(); } #endif if (!ret.empty()) return ret; // Drat! I have to do it the hard way. std::string pathEnvVar = GetEnv("PATH"); if (pathEnvVar.empty()) return ""; std::vector<std::string> pathDirs; bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs); if (!getDirList) return ""; std::vector<std::string>::const_iterator it = pathDirs.cbegin(); std::vector<std::string>::const_iterator itEnd = pathDirs.cend(); for ( ; it != itEnd; ++it) { boost::filesystem::path p(*it); p /= file; if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) { return p.make_preferred().string(); } } return ""; } std::string executable_path_fallback(const char *argv0) { if (argv0 == nullptr) return ""; if (argv0[0] == 0) return ""; #if (BOOST_OS_WINDOWS) const std::string os_sep("\\"); #else const std::string os_sep("/"); #endif if (strstr(argv0, os_sep.c_str()) != nullptr) { boost::system::error_code ec; boost::filesystem::path p( boost::filesystem::canonical( argv0, boost::filesystem::current_path(), ec)); if (ec.value() == boost::system::errc::success) { return p.make_preferred().string(); } } return search_path(argv0); } } } 

包括/升压/ executable_path.hpp

 #ifndef BOOST_EXECUTABLE_PATH_HPP_ #define BOOST_EXECUTABLE_PATH_HPP_ #pragma once #include <string> namespace boost { std::string executable_path(const char * argv0); } #endif // BOOST_EXECUTABLE_PATH_HPP_ 

包括/升压/细节/ executable_path_internals.hpp

 #ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_ #define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_ #pragma once #include <string> #include <vector> namespace boost { namespace detail { std::string GetEnv(const std::string& varName); bool GetDirectoryListFromDelimitedString( const std::string& str, std::vector<std::string>& dirs); std::string search_path(const std::string& file); std::string executable_path_fallback(const char * argv0); } } #endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_ 

我有一个完整的项目,包括在SnKOpen – / cpp / executable_path / trunk上提供的testing应用程序和CMake构build文件。

我已经在以下四种情况下testing了所有支持的操作系统上的应用程序。

  1. 相对path,在当前目录中可执行:即./executable_path_test
  2. 相对path,可在另一个目录中执行:即./build/executable_path_test
  3. 完整path:ie / some / dir / executable_path_test
  4. 可执行path,仅文件名:即executable_path_test

在所有四种情况下,executable_path和executable_path_fallback函数都可以工作,并在所有四种情况下返回相同的结果。 目前,当可执行文件位于当前目录中时,executable_path_fallback函数在Microsoft Windows系统上不会返回正确的值,并且只使用可执行文件的文件名。 我会很快解决这个问题。

笔记

这是对这个问题的更新答案。 我更新了答案,考虑到用户的意见和build议。 我还在SVN Repository中添加了一个项目链接。

我不确定Linux,但是在Windows上试试这个:

 #include <windows.h> #include <iostream> using namespace std ; int main() { char ownPth[MAX_PATH]; // When NULL is passed to GetModuleHandle, the handle of the exe itself is returned HMODULE hModule = GetModuleHandle(NULL); if (hModule != NULL) { // Use GetModuleFileName() with module handle to get the path GetModuleFileName(hModule, ownPth, (sizeof(ownPth))); cout << ownPth << endl ; system("PAUSE"); return 0; } else { cout << "Module handle is NULL" << endl ; system("PAUSE"); return 0; } } 

对于Windows:

GetModuleFileName – 返回exepath+ exe文件名

删除文件名
PathRemoveFileSpec

这是Windows特定的方式,但至less有一半是你的答案。

GetThisPath.h

 /// dest is expected to be MAX_PATH in length. /// returns dest /// TCHAR dest[MAX_PATH]; /// GetThisPath(dest, MAX_PATH); TCHAR* GetThisPath(TCHAR* dest, size_t destSize); 

GetThisPath.cpp

 #include <Shlwapi.h> #pragma comment(lib, "shlwapi.lib") TCHAR* GetThisPath(TCHAR* dest, size_t destSize) { if (!dest) return NULL; if (MAX_PATH > destSize) return NULL; DWORD length = GetModuleFileName( NULL, dest, destSize ); PathRemoveFileSpec(dest); return dest; } 

mainProgram.cpp

 TCHAR dest[MAX_PATH]; GetThisPath(dest, MAX_PATH); 

我build议使用平台检测作为预处理器指令来更改调用每个平台的GetThisPath的包装函数的实现。

对于Windows,您可以使用GetModuleFilename()。
对于Linux请参阅BinReloc 。

使用args [0]并查找'/'(或'\\'):

 #include <string> #include <iostream> // to show the result int main( int numArgs, char *args[]) { // Get the last position of '/' std::string aux(args[0]); // get '/' or '\\' depending on unix/mac or windows. #if defined(_WIN32) || defined(WIN32) int pos = aux.rfind('\\'); #else int pos = aux.rfind('/'); #endif // Get the path and the name std::string path = aux.substr(0,pos+1); std::string name = aux.substr(pos+1); // show results std::cout << "Path: " << path << std::endl; std::cout << "Name: " << name << std::endl; } 

编辑:如果“/”不存在,pos == – 1所以结果是正确的。

QT提供这个操作系统抽象为QCoreApplication :: applicationDirPath()

以下是一个快速和肮脏的解决scheme,但请注意,它远非万无一失:

 #include <iostream> using namespace std ; int main( int argc, char** argv) { cout << argv[0] << endl ; return 0; } 

那么我已经find了一个非常有趣的Linux用户(testing)的解决scheme,也可以用于Windows。

链接在这里

您可以通过在terminal中input以下命令在linux中创buildexe文件:g ++ -o test test.cpp

 char exePath[512]; CString strexePath; GetModuleFileName(NULL,exePath,512); strexePath.Format("%s",exePath); strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\')); 

在Unix(包括Linux)中尝试'哪个',在Windows中试试'where'。

 #include <stdio.h> #define _UNIX int main(int argc, char** argv) { char cmd[128]; char buf[128]; FILE* fp = NULL; #if defined(_UNIX) sprintf(cmd, "which %s > my.path", argv[0]); #else sprintf(cmd, "where %s > my.path", argv[0]); #endif system(cmd); fp = fopen("my.path", "r"); fgets(buf, sizeof(buf), fp); fclose(fp); printf("full path: %s\n", buf); unlink("my.path"); return 0; }