Получить путь к исполняемому файлу

Я знаю, что этот вопрос задавали раньше, но я до сих пор не нашел удовлетворительного ответа или окончательного «нет, этого нельзя сделать», поэтому я снова спрошу!

Все, что я хочу сделать, это получить путь к исполняемому исполняемому файлу в настоящий момент, либо как абсолютный путь, либо относительный к тому, откуда вызывается исполняемый файл, независимо от платформы. Я, хотя boost :: filesystem :: initial_path был ответом на мои проблемы, но, похоже, обрабатывает только «независимую от платформы» часть вопроса – он по-прежнему возвращает путь, из которого было вызвано приложение.

Для немного фона, это игра с использованием Ogre, которую я пытаюсь профилировать с помощью Very Sleepy, которая запускает целевой исполняемый файл из своего собственного каталога, поэтому, конечно, при загрузке игра не находит файлов конфигурации и т. Д. , Я хочу иметь возможность передать ему абсолютный путь к файлам конфигурации, которые, как я знаю, всегда будут жить вместе с исполняемым файлом. То же самое касается отладки в Visual Studio – я бы хотел запустить $ (TargetPath) без необходимости устанавливать рабочий каталог.

Я знаю, что нет кросс-платформенного способа.

Для Linux: readlink / proc / self / exe

Windows: GetModuleFileName

Этот способ использует boost + argv. Вы упомянули, что это не может быть кросс-платформой, поскольку оно может включать или не включать имя исполняемого файла. Ну, следующий код должен обойти это.

#include  #include  #include  namespace fs = boost::filesystem; int main(int argc,char** argv) { fs::path full_path( fs::initial_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  #include  #include  namespace fs = boost::filesystem; int main(int argc,char** argv) { //current working directory fs::path full_path( fs::current_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()

Функция boost :: dll :: program_location – один из лучших кросс-платформенных методов получения пути исполняемого исполняемого файла, который я знаю. Библиотека DLL была добавлена ​​в Boost в версии 1.61.0.

Следующее – мое решение. Я тестировал его на Windows, Mac OS X, Solaris, Free BSD и GNU / Linux.

Он требует Boost 1.55.0 или выше. Он использует библиотеку Boost.Filesystem напрямую, а библиотеку Boost.Locale и библиотеку Boost.System – косвенно.

SRC / executable_path.cpp

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0)) # include  #endif #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) # include  #endif #include  #include  namespace boost { #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) std::string executable_path(const char* argv0) { typedef std::vector char_vector; typedef std::vector::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  std::string executable_path(const char* argv0) { typedef std::vector char_vector; char_vector buf(1024, 0); uint32_t size = static_cast(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  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  std::string executable_path(const char* argv0) { typedef std::vector 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  std::string executable_path(const char *argv0) { typedef std::vector char_vector; typedef std::vector::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(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  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0)) # include  #endif #if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS) # include  #endif #include  #include  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_vector; typedef std::vector::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& dirs) { typedef boost::char_separator char_separator_type; typedef boost::tokenizer< boost::char_separator, 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 pathDirs; bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs); if (!getDirList) return ""; std::vector::const_iterator it = pathDirs.cbegin(); std::vector::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(); } } std::string ret = search_path(argv0); if (!ret.empty()) { return ret; } 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) { ret = p.make_preferred().string(); } return ret; } } } 

включить / усиление / executable_path.hpp

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

У меня есть полный проект, включая тестовое приложение и файлы сборки CMake, доступные в SnKOpen - / cpp / executable_path / trunk . Эта версия более полная, чем приведенная здесь версия. Он также поддерживает больше платформ.

Я тестировал приложение на всех поддерживаемых операционных системах в следующих четырех сценариях.

  1. Относительный путь, исполняемый в текущем каталоге: ie ./executable_path_test
  2. Относительный путь, исполняемый в другом каталоге: ie ./build/executable_path_test
  3. Полный путь: ie / some / dir / executable_path_test
  4. Исполняемый путь, только имя файла: ie executable_path_test

Во всех четырех сценариях работают как функции executable_path, так и executable_path_fallback и возвращают те же результаты.

Заметки

Это обновленный ответ на этот вопрос. Я обновил ответ, чтобы принять во внимание комментарии и предложения пользователей. Я также добавил ссылку на проект в моем репозитории SVN.

Я не уверен в Linux, но попробую это для Windows:

 #include  #include  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; } } 

Для окон:

GetModuleFileName – возвращает путь exe + exe filename

Чтобы удалить имя файла
PathRemoveFileSpec

Это особый способ Windows, но это, по крайней мере, половина вашего ответа.

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  #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); 

Я бы предложил использовать определение платформы в качестве препроцессорных директив для изменения реализации функции-обертки, которая вызывает GetThisPath для каждой платформы.

QT обеспечивает это абстракцией ОС как QCoreApplication :: applicationDirPath ()

Для Windows вы можете использовать GetModuleFilename ().
Для Linux см. BinReloc .

Используя args [0] и ищем ‘/’ (или ‘\\’):

 #include  #include  // 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; } 

EDITED: Если '/' не существует, pos == - 1, результат будет правильным.

Следующее работает как быстрое и грязное решение, но обратите внимание, что он далеко не надежный:

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

Я нашел очень интересное решение для пользователей Linux (проверено) и может работать и для окон.

Ссылка здесь

Вы можете создать exe-файл в linux, набрав его в терминале: g ++ -o test test.cpp

Это было мое решение в Windows. Он называется так:

 std::wstring sResult = GetPathOfEXE(64); 

Где 64 – минимальный размер, который, по вашему мнению, будет. GetPathOfEXE вызывает себя рекурсивно, каждый раз удваивая размер буфера, пока не получит достаточно большой буфер, чтобы получить весь путь без усечения.

 std::wstring GetPathOfEXE(DWORD dwSize) { WCHAR* pwcharFileNamePath; DWORD dwLastError; HRESULT hrError; std::wstring wsResult; DWORD dwCount; pwcharFileNamePath = new WCHAR[dwSize]; dwCount = GetModuleFileNameW( NULL, pwcharFileNamePath, dwSize ); dwLastError = GetLastError(); if (ERROR_SUCCESS == dwLastError) { hrError = PathCchRemoveFileSpec( pwcharFileNamePath, dwCount ); if (S_OK == hrError) { wsResult = pwcharFileNamePath; if (pwcharFileNamePath) { delete pwcharFileNamePath; } return wsResult; } else if(S_FALSE == hrError) { wsResult = pwcharFileNamePath; if (pwcharFileNamePath) { delete pwcharFileNamePath; } //there was nothing to truncate off the end of the path //returning something better than nothing in this case for the user return wsResult; } else { if (pwcharFileNamePath) { delete pwcharFileNamePath; } std::ostringstream oss; oss << "could not get file name and path of executing process. error truncating file name off path. last error : " << hrError; throw std::runtime_error(oss.str().c_str()); } } else if (ERROR_INSUFFICIENT_BUFFER == dwLastError) { if (pwcharFileNamePath) { delete pwcharFileNamePath; } return GetPathOfEXE( dwSize * 2 ); } else { if (pwcharFileNamePath) { delete pwcharFileNamePath; } std::ostringstream oss; oss << "could not get file name and path of executing process. last error : " << dwLastError; throw std::runtime_error(oss.str().c_str()); } } 

Если вам нужно обрабатывать пути Unicode для Windows:

 #include  #include  int wmain(int argc, wchar_t * argv[]) { HMODULE this_process_handle = GetModuleHandle(NULL); wchar_t this_process_path[MAX_PATH]; GetModuleFileNameW(NULL, this_process_path, sizeof(this_process_path)); std::wcout << "Unicode path of this app: " << this_process_path << std::endl; return 0; } 

В Windows у вас есть проблема, как удалить исполняемый файл из результата GetModuleFileName() . API Windows API PathRemoveFileSpec() который Nate использовал для этой цели в своем ответе, изменился между Windows 8 и его предшественниками. Итак, как оставаться совместимым с обоими и безопасными? К счастью, есть C ++ 17 (или Boost, если вы используете старый компилятор). Я делаю это:

 #include  #include  #include  namespace fs = std::experimental::filesystem; // We could use fs::path as return type, but if you're not aware of // std::experimental::filesystem, you probably handle filenames // as strings anyway in the remainder of your code. I'm on Japanese // Windows, so wide chars are a must. std::wstring getDirectoryWithCurrentExecutable() { int size = 256; std::vector charBuffer; // Let's be safe, and find the right buffer size programmatically. do { size *= 2; charBuffer.resize(size); // Resize until filename fits. GetModuleFileNameW returns the // number of characters written to the buffer, so if the // return value is smaller than the size of the buffer, it was // large enough. } while (GetModuleFileNameW(NULL, charBuffer.data(), size) == size); // Typically: c:/program files (x86)/something/foo/bar/exe/files/win64/baz.exe // (Note that windows supports forward and backward slashes as path // separators, so you have to be careful when searching through a path // manually.) // Let's extract the interesting part: fs::path path(charBuffer.data()); // Contains the full path including .exe return path.remove_filename() // Extract the directory ... .w_str(); // ... and convert to a string. } 

Этот метод работает как для Windows, так и для Linux:

 #include  #include  #ifdef _WIN32 #include  #define GetCurrentDir _getcwd #elif __linux__ #include  #define GetCurrentDir getcwd #endif std::string GetCurrentWorkingDir() { char buff[FILENAME_MAX]; GetCurrentDir(buff, FILENAME_MAX); std::string current_working_dir(buff); return current_working_dir; } 
 char exePath[512]; CString strexePath; GetModuleFileName(NULL,exePath,512); strexePath.Format("%s",exePath); strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\')); 

в Unix (включая Linux) попробуйте «which», в Windows попробуйте «где».

 #include  #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; } 
Давайте будем гением компьютера.