Get path of executable

前端 未结 23 1530
清歌不尽
清歌不尽 2020-11-22 07:04

I know this question has been asked before but I still haven\'t seen a satisfactory answer, or a definitive \"no, this cannot be done\", so I\'ll ask again!

All I wa

相关标签:
23条回答
  • 2020-11-22 07:30

    This was my solution in Windows. It is called like this:

    std::wstring sResult = GetPathOfEXE(64);
    

    Where 64 is the minimum size you think the path will be. GetPathOfEXE calls itself recursively, doubling the size of the buffer each time until it gets a big enough buffer to get the whole path without truncation.

    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());
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:31

    The boost::dll::program_location function is one of the best cross platform methods of getting the path of the running executable that I know of. The DLL library was added to Boost in version 1.61.0.

    The following is my solution. I have tested it on Windows, Mac OS X, Solaris, Free BSD, and GNU/Linux.

    It requires Boost 1.55.0 or greater. It uses the Boost.Filesystem library directly and the Boost.Locale library and Boost.System library indirectly.

    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/detail/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();
        }
      }
      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;
    }
    
    }
    }
    

    include/boost/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_
    

    include/boost/detail/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_
    

    I have a complete project, including a test application and CMake build files available at SnKOpen - /cpp/executable_path/trunk. This version is more complete than the version I provided here. It is also supports more platforms.

    I have tested the application on all supported operating systems in the following four scenarios.

    1. Relative path, executable in current directory: i.e. ./executable_path_test
    2. Relative path, executable in another directory: i.e. ./build/executable_path_test
    3. Full path: i.e. /some/dir/executable_path_test
    4. Executable in path, file name only: i.e. executable_path_test

    In all four scenarios, both the executable_path and executable_path_fallback functions work and return the same results.

    Notes

    This is an updated answer to this question. I updated the answer to take into consideration user comments and suggestions. I also added a link to a project in my SVN Repository.

    0 讨论(0)
  • 2020-11-22 07:31

    If using C++17 one can do the following to get the path to the executable.

    #include <filesystem>
    
    std::filesystem::path getExecutablePath()
    {
        return std::filesystem::canonical("/proc/self/exe");
    }
    

    The above answer has been tested on Debian 10 using G++ 9.3.0

    0 讨论(0)
  • 2020-11-22 07:33

    This is a Windows specific way, but it is at least half of your answer.

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

    I would suggest using platform detection as preprocessor directives to change the implementation of a wrapper function that calls GetThisPath for each platform.

    0 讨论(0)
  • 2020-11-22 07:33

    SDL2 (https://www.libsdl.org/) library has two functions implemented across a wide spectrum of platforms:

    • SDL_GetBasePath
    • SDL_GetPrefPath

    So if you don't want to reinvent the wheel... sadly, it means including the entire library, although it's got a quite permissive license and one could also just copy the code. Besides, it provides a lot of other cross-platform functionality.

    0 讨论(0)
  • 2020-11-22 07:33

    This method works for both Windows and Linux:

    #include <stdio.h>
    #include <string>
    #ifdef _WIN32
    #include <direct.h>
    #define GetCurrentDir _getcwd
    #elif __linux__
    #include <unistd.h>
    #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;
    }
    
    0 讨论(0)
提交回复
热议问题