获取可执行文件的路径

发布于 2024-08-07 09:05:20 字数 411 浏览 5 评论 0原文

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

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

作为一些背景知识,这是一个使用 Ogre 的游戏,我正在尝试使用 Very Sleepy 来分析它,它从自己的目录运行目标可执行文件,所以当然在加载时游戏找不到配置文件等并立即崩溃。我希望能够向其传递配置文件的绝对路径,我知道配置文件将始终与可执行文件一起存在。 Visual Studio 中的调试也是如此 - 我希望能够运行 $(TargetPath) 而无需设置工作目录。

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 want to do is get the path to the currently running executable, either as an absolute path or relative to where the executable is invoked from, in a platform-independent fashion. I though boost::filesystem::initial_path was the answer to my troubles but that seems to only handle the 'platform-independent' part of the question - it still returns the path from which the application was invoked.

For a bit of background, this is a game using Ogre, which I'm trying to profile using Very Sleepy, which runs the target executable from its own directory, so of course on load the game finds no configuration files etc. and promptly crashes. I want to be able to pass it an absolute path to the configuration files, which I know will always live alongside the executable. The same goes for debugging in Visual Studio - I'd like to be able to run $(TargetPath) without having to set the working directory.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(26

甜柠檬 2024-08-14 09:05:20

据我所知,没有跨平台的方法。

对于 Linux:将 "/proc/self/exe" 传递给 <代码>std::filesystem::canonicalreadlink< /代码>

Windows:将 NULL 作为模块句柄传递给 GetModuleFileName

There is no cross platform way that I know.

For Linux: pass "/proc/self/exe" to std::filesystem::canonical or readlink.

Windows: pass NULL as the module handle to GetModuleFileName.

混浊又暗下来 2024-08-14 09:05:20

boost::dll::program_location 函数是我所知道的获取正在运行的可执行文件路径的最佳跨平台方法之一。 DLL 库在 1.61.0 版本中添加到 Boost 中。

以下是我的解决方案。我已经在 Windows、Mac OS X、Solaris、Free BSD 和 GNU/Linux 上测试过它。

它需要 Boost 1.55.0 或更高版本。它直接使用 Boost.Filesystem 库 Boost.Locale 库和 <间接使用 href="http://www.boost.org/doc/libs/1_66_0/libs/system/doc/index.html" rel="noreferrer">Boost.System 库。

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_

我有一个完整的项目,包括一个测试应用程序和 CMake 构建文件,可在 SnKOpen - /cpp/executable_path/trunk。这个版本比我在这里提供的版本更完整。它还支持更多平台。

我在以下四种场景中在所有受支持的操作系统上测试了该应用程序。

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

在所有四种情况下,executable_path 和executable_path_fallback 函数都可以工作并返回相同的结果。

注释

这是此问题的更新答案。我更新了答案以考虑用户的意见和建议。我还在 SVN 存储库中添加了一个项目的链接。

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.

缺⑴份安定 2024-08-14 09:05:20

C++17、windows、unicode、使用文件系统新 api:(

#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;

int wmain(int argc, wchar_t** argv)
{
    auto dir = weakly_canonical(path(argv[0])).parent_path();
    printf("%S", dir.c_str());
    return 0;
}

重要:wmainwchar_t** 一起使用 - 不要混合 < code>main 和 wchar_t**。对于 cmake 项目,使用 add_definitions(-DUNICODE -D_UNICODE) 启用 unicode。

怀疑这个解决方案应该是可移植的,但不知道 unicode 在其他操作系统上是如何实现的。

仅当您使用输出目录上层文件夹引用(“..”)来简化路径时,才需要weakly_canonical。如果您不使用它 - 将其删除。

如果您是从动态链接库(.dll /.so)进行操作,那么您可能没有argv,那么您可以考虑以下解决方案:

application.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}

C++17, windows, unicode, using filesystem new api:

#include "..\Project.h"
#include <filesystem>
using namespace std;
using namespace filesystem;

int wmain(int argc, wchar_t** argv)
{
    auto dir = weakly_canonical(path(argv[0])).parent_path();
    printf("%S", dir.c_str());
    return 0;
}

(Important: Use wmain with wchar_t** - don't mix main with wchar_t**. For cmake projects enable unicode using add_definitions(-DUNICODE -D_UNICODE)).

Suspect this solution should be portable, but don't know how unicode is implemented on other OS's.

weakly_canonical is needed only if you use as Output Directory upper folder references ('..') to simplify path. If you don't use it - remove it.

If you're operating from dynamic link library (.dll /.so), then you might not have argv, then you can consider following solution:

application.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}
尴尬癌患者 2024-08-14 09:05:20

这种方式使用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()

This way uses boost + argv. You mentioned this may not be cross platform because it may or may not include the executable name. Well the following code should work around that.

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

The following code gets the current working directory which may do what you need

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

Note
Just realized that basename() was deprecated so had to switch to .stem()

×纯※雪 2024-08-14 09:05:20

我不确定 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;
     }
}

I'm not sure about Linux, but try this for 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;
     }
}
昇り龍 2024-08-14 09:05:20

这就是我最终得到的

头文件如下所示:

#pragma once

#include <string>
namespace MyPaths {

  std::string getExecutablePath();
  std::string getExecutableDir();
  std::string mergePaths(std::string pathA, std::string pathB);
  bool checkIfFileExists (const std::string& filePath);

}

实现


#if defined(_WIN32)
    #include <windows.h>
    #include <Shlwapi.h>
    #include <io.h> 

    #define access _access_s
#endif

#ifdef __APPLE__
    #include <libgen.h>
    #include <limits.h>
    #include <mach-o/dyld.h>
    #include <unistd.h>
#endif

#ifdef __linux__
    #include <limits.h>
    #include <libgen.h>
    #include <unistd.h>

    #if defined(__sun)
        #define PROC_SELF_EXE "/proc/self/path/a.out"
    #else
        #define PROC_SELF_EXE "/proc/self/exe"
    #endif

#endif

namespace MyPaths {

#if defined(_WIN32)

std::string getExecutablePath() {
   char rawPathName[MAX_PATH];
   GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
   return std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char* exePath = new char[executablePath.length()];
    strcpy(exePath, executablePath.c_str());
    PathRemoveFileSpecA(exePath);
    std::string directory = std::string(exePath);
    delete[] exePath;
    return directory;
}

std::string mergePaths(std::string pathA, std::string pathB) {
  char combined[MAX_PATH];
  PathCombineA(combined, pathA.c_str(), pathB.c_str());
  std::string mergedPath(combined);
  return mergedPath;
}

#endif

#ifdef __linux__

std::string getExecutablePath() {
   char rawPathName[PATH_MAX];
   realpath(PROC_SELF_EXE, rawPathName);
   return  std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char *executablePathStr = new char[executablePath.length() + 1];
    strcpy(executablePathStr, executablePath.c_str());
    char* executableDir = dirname(executablePathStr);
    delete [] executablePathStr;
    return std::string(executableDir);
}

std::string mergePaths(std::string pathA, std::string pathB) {
  return pathA+"/"+pathB;
}

#endif

#ifdef __APPLE__
    std::string getExecutablePath() {
        char rawPathName[PATH_MAX];
        char realPathName[PATH_MAX];
        uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);

        if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
            realpath(rawPathName, realPathName);
        }
        return  std::string(realPathName);
    }

    std::string getExecutableDir() {
        std::string executablePath = getExecutablePath();
        char *executablePathStr = new char[executablePath.length() + 1];
        strcpy(executablePathStr, executablePath.c_str());
        char* executableDir = dirname(executablePathStr);
        delete [] executablePathStr;
        return std::string(executableDir);
    }

    std::string mergePaths(std::string pathA, std::string pathB) {
        return pathA+"/"+pathB;
    }
#endif


bool checkIfFileExists (const std::string& filePath) {
   return access( filePath.c_str(), 0 ) == 0;
}

}

This is what I ended up with

The header file looks like this:

#pragma once

#include <string>
namespace MyPaths {

  std::string getExecutablePath();
  std::string getExecutableDir();
  std::string mergePaths(std::string pathA, std::string pathB);
  bool checkIfFileExists (const std::string& filePath);

}

Implementation


#if defined(_WIN32)
    #include <windows.h>
    #include <Shlwapi.h>
    #include <io.h> 

    #define access _access_s
#endif

#ifdef __APPLE__
    #include <libgen.h>
    #include <limits.h>
    #include <mach-o/dyld.h>
    #include <unistd.h>
#endif

#ifdef __linux__
    #include <limits.h>
    #include <libgen.h>
    #include <unistd.h>

    #if defined(__sun)
        #define PROC_SELF_EXE "/proc/self/path/a.out"
    #else
        #define PROC_SELF_EXE "/proc/self/exe"
    #endif

#endif

namespace MyPaths {

#if defined(_WIN32)

std::string getExecutablePath() {
   char rawPathName[MAX_PATH];
   GetModuleFileNameA(NULL, rawPathName, MAX_PATH);
   return std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char* exePath = new char[executablePath.length()];
    strcpy(exePath, executablePath.c_str());
    PathRemoveFileSpecA(exePath);
    std::string directory = std::string(exePath);
    delete[] exePath;
    return directory;
}

std::string mergePaths(std::string pathA, std::string pathB) {
  char combined[MAX_PATH];
  PathCombineA(combined, pathA.c_str(), pathB.c_str());
  std::string mergedPath(combined);
  return mergedPath;
}

#endif

#ifdef __linux__

std::string getExecutablePath() {
   char rawPathName[PATH_MAX];
   realpath(PROC_SELF_EXE, rawPathName);
   return  std::string(rawPathName);
}

std::string getExecutableDir() {
    std::string executablePath = getExecutablePath();
    char *executablePathStr = new char[executablePath.length() + 1];
    strcpy(executablePathStr, executablePath.c_str());
    char* executableDir = dirname(executablePathStr);
    delete [] executablePathStr;
    return std::string(executableDir);
}

std::string mergePaths(std::string pathA, std::string pathB) {
  return pathA+"/"+pathB;
}

#endif

#ifdef __APPLE__
    std::string getExecutablePath() {
        char rawPathName[PATH_MAX];
        char realPathName[PATH_MAX];
        uint32_t rawPathSize = (uint32_t)sizeof(rawPathName);

        if(!_NSGetExecutablePath(rawPathName, &rawPathSize)) {
            realpath(rawPathName, realPathName);
        }
        return  std::string(realPathName);
    }

    std::string getExecutableDir() {
        std::string executablePath = getExecutablePath();
        char *executablePathStr = new char[executablePath.length() + 1];
        strcpy(executablePathStr, executablePath.c_str());
        char* executableDir = dirname(executablePathStr);
        delete [] executablePathStr;
        return std::string(executableDir);
    }

    std::string mergePaths(std::string pathA, std::string pathB) {
        return pathA+"/"+pathB;
    }
#endif


bool checkIfFileExists (const std::string& filePath) {
   return access( filePath.c_str(), 0 ) == 0;
}

}

蓝色星空 2024-08-14 09:05:20

对于 Windows:

GetModuleFileName - 返回 exe 路径 + exe 文件名

删除文件名
PathRemoveFileSpec

For windows:

GetModuleFileName - returns the exe path + exe filename

To remove filename
PathRemoveFileSpec

QT 提供了操作系统抽象 QCoreApplication::applicationDirPath()

QT provides this with OS abstraction as QCoreApplication::applicationDirPath()

北凤男飞 2024-08-14 09:05:20

如果使用 C++17,可以执行以下操作来获取可执行文件的路径。

#include <filesystem>

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

以上答案已在 Debian 10 上使用 G++ 9.3.0 进行了测试

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

夏末 2024-08-14 09:05:20

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

我建议使用平台检测作为预处理器指令来更改调用 < 的包装函数的实现对于每个平台,code>GetThisPath。

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.

笑脸一如从前 2024-08-14 09:05:20

使用 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 所以结果是正确的。

Using args[0] and looking for '/' (or '\\'):

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

EDITED:
If '/' does not exist, pos==-1 so the result is correct.

风情万种。 2024-08-14 09:05:20

对于 Windows,您可以使用 GetModuleFilename()。
对于 Linux,请参阅 BinReloc(旧的、已失效的 URL) 镜像href="https://github.com/datenwolf/binreloc" rel="nofollow noreferrer">datenwolf 的 GitHub 存储库中的 BinReloc。

For Windows you can use GetModuleFilename().
For Linux see BinReloc (old, defunct URL) mirror of BinReloc in datenwolf's GitHub repositories.

地狱即天堂 2024-08-14 09:05:20

这可能是最自然的方法,同时涵盖了大多数主要桌面平台。我不确定,但我相信如果您更改平台宏检查以覆盖所有 BSD,这应该适用于所有 BSD,而不仅仅是 FreeBSD。如果我有时间安装 Solaris,我一定会将该平台添加到受支持的列表中。

在 Windows 上具有完整的 UTF-8 支持,但并不是每个人都足够关心这一点。

procinfo/win32/procinfo.cpp

#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>

using std::string;
using std::wstring;
using std::vector;
using std::size_t;

static inline string narrow(wstring wstr) {
  int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
  vector<char> buf(nbytes);
  return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}

process_t ppid_from_pid(process_t pid) {        
  process_t ppid;       
  HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);      
  PROCESSENTRY32 pe = { 0 };        
  pe.dwSize = sizeof(PROCESSENTRY32);       
  if (Process32First(hp, &pe)) {        
    do {        
      if (pe.th32ProcessID == pid) {        
        ppid = pe.th32ParentProcessID;      
        break;      
      }     
    } while (Process32Next(hp, &pe));       
  }     
  CloseHandle(hp);      
  return ppid;      
}

string path_from_pid(process_t pid) {
  string path;
  HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
  MODULEENTRY32W me = { 0 };
  me.dwSize = sizeof(MODULEENTRY32W);
  if (Module32FirstW(hm, &me)) {
    do {
      if (me.th32ProcessID == pid) {
        path = narrow(me.szExePath);
        break;
      }
    } while (Module32NextW(hm, &me));
  }
  CloseHandle(hm);
  return path;
}
#endif

procinfo/macosx/procinfo.cpp

#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>

using std::string;

string path_from_pid(process_t pid) {
  string path;
  char buffer[PROC_PIDPATHINFO_MAXSIZE];
  if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
    path = string(buffer) + "\0";
  }
  return path;
}
#endif

procinfo/linux/procinfo.cpp

#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>

using std::string;
using std::to_string;

string path_from_pid(process_t pid) {
  string path;
  string link = string("/proc/") + to_string(pid) + string("/exe");
  char *buffer = realpath(link.c_str(), NULL);
  path = buffer ? : "";
  free(buffer);
  return path;
}
#endif

procinfo/freebsd/procinfo.cpp

#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>

using std::string;
using std::size_t;

string path_from_pid(process_t pid) {
  string path;
  size_t length;
  // CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
  if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
    path.resize(length, '\0');
    char *buffer = path.data();
    if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
      path = string(buffer) + "\0";
    }
  }
  return path;
}
#endif

procinfo/procinfo.cpp

#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>

using std::string;
using std::size_t;

process_t pid_from_self() {
  #ifdef _WIN32
  return _getpid();
  #else
  return getpid();
  #endif
}

process_t ppid_from_self() {
  #ifdef _WIN32
  return ppid_from_pid(pid_from_self());
  #else
  return getppid();
  #endif
}

string dir_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(0, fp + 1);
}

string name_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(fp + 1);
}

procinfo/procinfo.h

#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>

/* windows-only helper function */
process_t ppid_from_pid(process_t pid);

/* get current process process id */
process_t pid_from_self();

/* get parent process process id */
process_t ppid_from_self();

/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);

/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);

/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);

这允许获取几乎所有可执行文件的完整路径任何进程 ID,除了 Windows 上有一些具有安全属性的进程根本不允许它,所以所见即所得,这个解决方案并不完美。

为了更准确地解决问题,您可以这样做:

procinfo.cpp

#include "procinfo/procinfo.h"
#include <iostream>

using std::string;
using std::cout;
using std::endl;

int main() {
  cout << dir_from_pid(pid_from_self()) << endl;
  return 0;
}

使用以下命令构建上述文件结构:

procinfo.sh

cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe

用于下载上面列出的文件的副本:

git clone git://github.com/time-killer-games/procinfo.git

对于更多与跨平台进程相关的优点:

https://github.com/time-killer-games/enigma-dev

请参阅自述文件以获取所包含的大部分功能的列表。

This is probably the most natural way to do it, while covering most major desktop platforms. I am not certain, but I believe this should work with all the BSD's, not just FreeBSD, if you change the platform macro check to cover all of them. If I ever get around to installing Solaris, I'll be sure to add that platform to the supported list.

Features full UTF-8 support on Windows, which not everyone cares enough to go that far.

procinfo/win32/procinfo.cpp

#ifdef _WIN32
#include "../procinfo.h"
#include <windows.h>
#include <tlhelp32.h>
#include <cstddef>
#include <vector>
#include <cwchar>

using std::string;
using std::wstring;
using std::vector;
using std::size_t;

static inline string narrow(wstring wstr) {
  int nbytes = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);
  vector<char> buf(nbytes);
  return string{ buf.data(), (size_t)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), buf.data(), nbytes, NULL, NULL) };
}

process_t ppid_from_pid(process_t pid) {        
  process_t ppid;       
  HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);      
  PROCESSENTRY32 pe = { 0 };        
  pe.dwSize = sizeof(PROCESSENTRY32);       
  if (Process32First(hp, &pe)) {        
    do {        
      if (pe.th32ProcessID == pid) {        
        ppid = pe.th32ParentProcessID;      
        break;      
      }     
    } while (Process32Next(hp, &pe));       
  }     
  CloseHandle(hp);      
  return ppid;      
}

string path_from_pid(process_t pid) {
  string path;
  HANDLE hm = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
  MODULEENTRY32W me = { 0 };
  me.dwSize = sizeof(MODULEENTRY32W);
  if (Module32FirstW(hm, &me)) {
    do {
      if (me.th32ProcessID == pid) {
        path = narrow(me.szExePath);
        break;
      }
    } while (Module32NextW(hm, &me));
  }
  CloseHandle(hm);
  return path;
}
#endif

procinfo/macosx/procinfo.cpp

#if defined(__APPLE__) && defined(__MACH__)
#include "../procinfo.h"
#include <libproc.h>

using std::string;

string path_from_pid(process_t pid) {
  string path;
  char buffer[PROC_PIDPATHINFO_MAXSIZE];
  if (proc_pidpath(pid, buffer, sizeof(buffer)) > 0) {
    path = string(buffer) + "\0";
  }
  return path;
}
#endif

procinfo/linux/procinfo.cpp

#ifdef __linux__
#include "../procinfo.h"
#include <cstdlib>

using std::string;
using std::to_string;

string path_from_pid(process_t pid) {
  string path;
  string link = string("/proc/") + to_string(pid) + string("/exe");
  char *buffer = realpath(link.c_str(), NULL);
  path = buffer ? : "";
  free(buffer);
  return path;
}
#endif

procinfo/freebsd/procinfo.cpp

#ifdef __FreeBSD__
#include "../procinfo.h"
#include <sys/sysctl.h>
#include <cstddef>

using std::string;
using std::size_t;

string path_from_pid(process_t pid) {
  string path;
  size_t length;
  // CTL_KERN::KERN_PROC::KERN_PROC_PATHNAME(pid)
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid };
  if (sysctl(mib, 4, NULL, &length, NULL, 0) == 0) {
    path.resize(length, '\0');
    char *buffer = path.data();
    if (sysctl(mib, 4, buffer, &length, NULL, 0) == 0) {
      path = string(buffer) + "\0";
    }
  }
  return path;
}
#endif

procinfo/procinfo.cpp

#include "procinfo.h"
#ifdef _WiN32
#include <process.h>
#endif
#include <unistd.h>
#include <cstddef>

using std::string;
using std::size_t;

process_t pid_from_self() {
  #ifdef _WIN32
  return _getpid();
  #else
  return getpid();
  #endif
}

process_t ppid_from_self() {
  #ifdef _WIN32
  return ppid_from_pid(pid_from_self());
  #else
  return getppid();
  #endif
}

string dir_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(0, fp + 1);
}

string name_from_pid(process_t pid) {
  string fname = path_from_pid(pid);
  size_t fp = fname.find_last_of("/\\");
  return fname.substr(fp + 1);
}

procinfo/procinfo.h

#ifdef _WiN32
#include <windows.h>
typedef DWORD process_t;
#else
#include <sys/types.h>
typedef pid_t process_t;
#endif
#include <string>

/* windows-only helper function */
process_t ppid_from_pid(process_t pid);

/* get current process process id */
process_t pid_from_self();

/* get parent process process id */
process_t ppid_from_self();

/* std::string possible_result = "C:\\path\\to\\file.exe"; */
std::string path_from_pid(process_t pid);

/* std::string possible_result = "C:\\path\\to\\"; */
std::string dir_from_pid(process_t pid);

/* std::string possible_result = "file.exe"; */
std::string name_from_pid(process_t pid);

This allows getting the full path to the executable of pretty much any process id, except on Windows there are some process's with security attributes which simply will not allow it, so wysiwyg, this solution is not perfect.

To address what the question was asking more precisely, you may do this:

procinfo.cpp

#include "procinfo/procinfo.h"
#include <iostream>

using std::string;
using std::cout;
using std::endl;

int main() {
  cout << dir_from_pid(pid_from_self()) << endl;
  return 0;
}

Build the above file structure with this command:

procinfo.sh

cd "${0%/*}"
g++ procinfo.cpp procinfo/procinfo.cpp procinfo/win32/procinfo.cpp procinfo/macosx/procinfo.cpp procinfo/linux/procinfo.cpp procinfo/freebsd/procinfo.cpp -o procinfo.exe

For downloading a copy of the files listed above:

git clone git://github.com/time-killer-games/procinfo.git

For more cross-platform process-related goodness:

https://github.com/time-killer-games/enigma-dev

See the readme for a list of most of the functions included.

半边脸i 2024-08-14 09:05:20

有几个答案建议使用 Windows 上的 GetModuleFileName。这些答案有一些缺点,例如:

  • 代码应该适用于 UNICODE 和 ANSI 版本
  • 路径可能比 MAX_PATH 长
  • GetModuleFileName 函数可能会失败并返回 0。
  • GetModuleFileName 可能会返回相对可执行文件名称而不是全名
  • GetModuleFileName 可能会返回一个短路径,例如 C:\GIT-RE~1\TEST_G~1\test.exe

让我提供一个改进版本,考虑了上述几点:

#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>

// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
    // First we need to get a length of the full name string
    const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
    if (full_name_length == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
    const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
    if (res == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The full name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(full_name_buffer.get());
}

// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
    // First we need to get a length of the long name string
    const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
    if (long_name_length == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
    const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
    if (res == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The long name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(long_name_buffer.get());
}

std::basic_string<TCHAR> get_current_executable_full_name()
{
    DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that 
                                       // the path doesn't exceeds 260 characters
    std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};

    while (true) {
        const auto bytes_written = GetModuleFileName(
            NULL, path_buffer.get(), path_buffer_size);
        const auto last_error = GetLastError();

        if (bytes_written == 0) {
            // GetModuleFileName call failed. Maybe you want to throw an exception.
            return std::basic_string<TCHAR>{};
        }

        if (last_error == ERROR_INSUFFICIENT_BUFFER) {
            // There is not enough space in our buffer to fit the path.
            // We need to increase the buffer and try again.
            path_buffer_size *= 2;
            path_buffer.reset(new TCHAR[path_buffer_size]);
            continue;
        }

        // GetModuleFileName has successfully written the executable name to the buffer.
        // Now we need to convert it to a full long name
        std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
        return get_long_name(full_name.c_str());
    }
}

// Example of how this function can be used
int main()
{
#ifdef UNICODE
    // If you use UNICODE version of WinApi
    std::wstring exe_file_full_name = get_current_executable_full_name();
    std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
    std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
    // If you use ANSI version of WinApi
    std::string exe_file_full_name = get_current_executable_full_name();
    std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
    std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}

There are several answers recommending using GetModuleFileName on Windows. These answers have some shortcomings like:

  • The code should work for both UNICODE and ANSI versions
  • The path can be longer than MAX_PATH
  • The GetModuleFileName function may fail and return 0.
  • GetModuleFileName may return a relative executable name instead of a full name
  • GetModuleFileName may return a short path like C:\GIT-RE~1\TEST_G~1\test.exe

Let me provide an improved version, which takes into account the abovementioned points:

#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>

// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
    // First we need to get a length of the full name string
    const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
    if (full_name_length == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
    const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
    if (res == 0) {
        // GetFullPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The full name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(full_name_buffer.get());
}

// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
    // First we need to get a length of the long name string
    const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
    if (long_name_length == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // Now, when we know the length, we create a buffer with correct size and write the full name into it
    std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
    const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
    if (res == 0) {
        // GetLongPathName call failed. Maybe you want to throw an exception.
        return std::basic_string<TCHAR>{};
    }

    // The long name has been successfully written to the buffer.
    return std::basic_string<TCHAR>(long_name_buffer.get());
}

std::basic_string<TCHAR> get_current_executable_full_name()
{
    DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that 
                                       // the path doesn't exceeds 260 characters
    std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};

    while (true) {
        const auto bytes_written = GetModuleFileName(
            NULL, path_buffer.get(), path_buffer_size);
        const auto last_error = GetLastError();

        if (bytes_written == 0) {
            // GetModuleFileName call failed. Maybe you want to throw an exception.
            return std::basic_string<TCHAR>{};
        }

        if (last_error == ERROR_INSUFFICIENT_BUFFER) {
            // There is not enough space in our buffer to fit the path.
            // We need to increase the buffer and try again.
            path_buffer_size *= 2;
            path_buffer.reset(new TCHAR[path_buffer_size]);
            continue;
        }

        // GetModuleFileName has successfully written the executable name to the buffer.
        // Now we need to convert it to a full long name
        std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
        return get_long_name(full_name.c_str());
    }
}

// Example of how this function can be used
int main()
{
#ifdef UNICODE
    // If you use UNICODE version of WinApi
    std::wstring exe_file_full_name = get_current_executable_full_name();
    std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
    std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
    // If you use ANSI version of WinApi
    std::string exe_file_full_name = get_current_executable_full_name();
    std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
    std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
    std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}
怀念你的温柔 2024-08-14 09:05:20

正如其他人提到的,argv[0] 是一个非常好的解决方案,前提是平台实际上传递了可执行文件路径,这肯定不比操作系统是 Windows 的可能性小(其中 WinAPI 可以帮助找到可执行文件)小路)。如果您想删除字符串以仅包含可执行文件所在目录的路径,那么使用该路径查找其他应用程序文件(例如游戏资产,如果您的程序是游戏)是完全可以的,因为打开文件是相对于工作目录,或者根目录(如果提供)。

As others mentioned, argv[0] is quite a nice solution, provided that the platform actually passes the executable path, which is surely not less probable than the OS being Windows (where WinAPI can help find the executable path). If you want to strip the string to only include the path to the directory where the executable resides, then using that path to find other application files (like game assets if your program is a game) is perfectly fine, since opening files is relative to the working directory, or, if provided, the root.

放肆 2024-08-14 09:05:20

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

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}

The following works as a quick and dirty solution, but note that it is far from being foolproof:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}
流殇 2024-08-14 09:05:20

如果您需要处理 Windows 的 unicode 路径:

#include <Windows.h>
#include <iostream>

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

In case you need to handle unicode paths for Windows:

#include <Windows.h>
#include <iostream>

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;
}
狂之美人 2024-08-14 09:05:20

这是我的简单解决方案,适用于 Windows 和 Linux,基于 此解决方案 并使用 这个答案

#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux

string replace(const string& s, const string& from, const string& to) {
    string r = s;
    int p = 0;
    while((p=(int)r.find(from, p))!=string::npos) {
        r.replace(p, from.length(), to);
        p += (int)to.length();
    }
    return r;
}
string get_exe_path() { // returns path where executable is located
    string path = "";
#if defined(_WIN32)
    wchar_t wc[260] = {0};
    GetModuleFileNameW(NULL, wc, 260);
    wstring ws(wc);
    transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
    path = replace(path, "\\", "/");
#elif defined(__linux__)
    char c[260];
    int length = (int)readlink("/proc/self/exe", c, 260);
    path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
    return path.substr(0, path.rfind('/')+1);
}

Here my simple solution that works in both Windows and Linux, based on this solution and modified with this answer:

#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux

string replace(const string& s, const string& from, const string& to) {
    string r = s;
    int p = 0;
    while((p=(int)r.find(from, p))!=string::npos) {
        r.replace(p, from.length(), to);
        p += (int)to.length();
    }
    return r;
}
string get_exe_path() { // returns path where executable is located
    string path = "";
#if defined(_WIN32)
    wchar_t wc[260] = {0};
    GetModuleFileNameW(NULL, wc, 260);
    wstring ws(wc);
    transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
    path = replace(path, "\\", "/");
#elif defined(__linux__)
    char c[260];
    int length = (int)readlink("/proc/self/exe", c, 260);
    path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
    return path.substr(0, path.rfind('/')+1);
}
陪我终i 2024-08-14 09:05:20

对于 Windows,您会遇到如何从 GetModuleFileName() 结果中删除可执行文件的问题。 Nate 在其答案中为此目的使用的 Windows API 调用 PathRemoveFileSpec() 在 Windows 8 及其前身之间发生了变化。那么如何保持两者兼容又安全呢?幸运的是,有 C++17(或者 Boost,如果您使用的是较旧的编译器)。我这样做:

#include <windows.h>
#include <string>
#include <filesystem>
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<wchar_t> 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.
}

For Windows, you have the problem of how to strip the executable from the result of GetModuleFileName(). The Windows API call PathRemoveFileSpec() that Nate used for that purpose in his answer changed between Windows 8 and its predecessors. So how to remain compatible with both and safe? Luckily, there's C++17 (or Boost, if you're using an older compiler). I do this:

#include <windows.h>
#include <string>
#include <filesystem>
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<wchar_t> 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.
}
小镇女孩 2024-08-14 09:05:20

SDL2 (https://www.libsdl.org/) 库有两个函数在广泛的领域实现平台:

  • SDL_GetBasePath
  • SDL_GetPrefPath

因此,如果您不想重新发明轮子...可悲的是,这意味着包括整个库,尽管它有一个相当宽松的许可证,并且也可以复制代码。此外,它还提供了许多其他跨平台功能。

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.

月光色 2024-08-14 09:05:20

我没有阅读我的解决方案是否已发布,但在 Linux 和 osx 上,您可以在主函数中读取 0 参数,如下所示:

int main(int argument_count, char **argument_list) {
    std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
    std::size_t pos = currentWorkingDirectoryPath.rfind("/");      // position of "live" in str
    currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);

在 argument_list 的第一项中,可执行文件的名称已集成,但被上面的代码删除。

I didn't read if my solution is already posted but on linux and osx you can read the 0 argument in your main function like this:

int main(int argument_count, char **argument_list) {
    std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
    std::size_t pos = currentWorkingDirectoryPath.rfind("/");      // position of "live" in str
    currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);

In the first item of argument_list the name of the executable is integrated but removed by the code above.

〃温暖了心ぐ 2024-08-14 09:05:20

有一个专门用于此目的的 C 库:whereami.c

#include <filesystem>
#include <string>

#include "whereami.h"

auto get_current_exe_path() -> std::string {
    int length = wai_getExecutablePath(NULL, 0, NULL);
    int dirname_length = 0;

    char* cstr = static_cast<char*>(malloc(length + 1));
    wai_getExecutablePath(cstr, length, &dirname_length);
    cstr[length] = '\0';

    std::string result(cstr);

    free(cstr);
    cstr = NULL;

    return result;
}

支持的平台:

  • Windows
  • Linux
  • Mac
  • iOS
  • Android
  • QNX
  • Neutrino
  • FreeBSD
  • NetBSD
  • DragonFly BSD
  • SunOS
  • OpenBSD

There's a C library dedicated to this: whereami.c.

#include <filesystem>
#include <string>

#include "whereami.h"

auto get_current_exe_path() -> std::string {
    int length = wai_getExecutablePath(NULL, 0, NULL);
    int dirname_length = 0;

    char* cstr = static_cast<char*>(malloc(length + 1));
    wai_getExecutablePath(cstr, length, &dirname_length);
    cstr[length] = '\0';

    std::string result(cstr);

    free(cstr);
    cstr = NULL;

    return result;
}

Supported platforms:

  • Windows
  • Linux
  • Mac
  • iOS
  • Android
  • QNX
  • Neutrino
  • FreeBSD
  • NetBSD
  • DragonFly BSD
  • SunOS
  • OpenBSD
昵称有卵用 2024-08-14 09:05:20

这是我在 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());
    }
}

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());
    }
}
﹏半生如梦愿梦如真 2024-08-14 09:05:20
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
久随 2024-08-14 09:05:20

在 Unix(包括 Linux)中尝试“which”,在 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;
}

in Unix(including Linux) try 'which', in Windows try '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;
}
清风挽心 2024-08-14 09:05:20

从 C++17 开始:

确保包含 std 文件系统。

#include <filesystem>

现在你可以做到这一点。

std::filesystem::current_path().string()

boost 文件系统成为标准库的一部分。

如果您找不到它,请尝试查看以下内容:

std::experimental::filesystem

As of C++17:

Make sure you include std filesystem.

#include <filesystem>

and now you can do this.

std::filesystem::current_path().string()

boost filesystem became part of the standard lib.

if you can't find it try to look under:

std::experimental::filesystem
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文