รับเส้นทางของปฏิบัติการ


115

ฉันรู้ว่ามีการถามคำถามนี้มาก่อน แต่ฉันยังไม่เห็นคำตอบที่น่าพอใจหรือมีคำว่า "ไม่ทำไม่ได้" ดังนั้นฉันจะถามอีกครั้ง!

สิ่งที่ฉันต้องการทำคือรับพา ธ ไปยังไฟล์ปฏิบัติการที่กำลังทำงานอยู่ไม่ว่าจะเป็นพา ธ สัมบูรณ์หรือสัมพันธ์กับตำแหน่งที่เรียกใช้ไฟล์ปฏิบัติการในรูปแบบที่ไม่ขึ้นกับแพลตฟอร์ม ฉันแม้ว่า boost :: filesystem :: initial_path เป็นคำตอบสำหรับปัญหาของฉัน แต่ดูเหมือนว่าจะจัดการเฉพาะส่วนที่ 'ไม่ขึ้นกับแพลตฟอร์ม' ของคำถาม - มันยังคงส่งคืนเส้นทางที่แอปพลิเคชันถูกเรียกใช้

สำหรับพื้นหลังเล็กน้อยนี่เป็นเกมที่ใช้ Ogre ซึ่งฉันกำลังพยายามสร้างโปรไฟล์โดยใช้ Very Sleepy ซึ่งเรียกใช้เป้าหมายที่เรียกใช้งานได้จากไดเรกทอรีของตัวเองดังนั้นแน่นอนว่าเมื่อโหลดเกมจะไม่พบไฟล์การกำหนดค่า ฯลฯ และเกิดปัญหาทันที . ฉันต้องการที่จะส่งผ่านเส้นทางที่แน่นอนไปยังไฟล์กำหนดค่าซึ่งฉันรู้ว่าจะอยู่คู่กับไฟล์ปฏิบัติการเสมอ เช่นเดียวกับการดีบักใน Visual Studio - ฉันต้องการที่จะสามารถเรียกใช้ $ (TargetPath) โดยไม่ต้องตั้งค่าไดเร็กทอรีการทำงาน



9
โปรดทราบว่าเป็นไปไม่ได้ที่จะพิสูจน์ว่าไม่มีคำตอบดังนั้นคุณจึงไม่สามารถหาNO ที่ชัดเจนได้ ฉันยินดีที่จะให้หมายเลขที่เชื่อถือได้ :)
MSalters


" เมื่อโหลดเกมไม่พบไฟล์การกำหนดค่า ฯลฯ " ดังนั้นเกมจึงค้นหาไฟล์การกำหนดค่าในไดเรกทอรีปัจจุบัน? นั่นเป็นความคิดที่ไม่ดีและอาจเป็นช่องโหว่ด้านความปลอดภัย ไฟล์คอนฟิกูเรชันควรจัดเก็บในตำแหน่งมาตรฐาน
ซอกแซก

1
ฉันโพสต์คำตอบไว้ที่นี่สำหรับคำถามที่เกี่ยวข้องซึ่งตอบคุณได้เช่นกันทำงานข้ามแพลตฟอร์มโดยใช้ boost
jtbr

คำตอบ:


86

ไม่มีทางข้ามแพลตฟอร์มที่ฉันรู้

สำหรับ Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
ความเป็นอิสระของแพลตฟอร์มเป็นเพียงการซ่อนการพึ่งพาแพลตฟอร์ม ในกรณีนี้การใช้มาโครระบบปฏิบัติการที่กำหนดไว้ล่วงหน้าซึ่งมีรายละเอียดที่predef.sourceforge.net/preos.htmlเพื่อเลือกวิธีการนั้นตรงไปตรงมา
Clifford

4
นี่คือสิ่งที่ทุกคนทำเมื่อใดก็ตามที่พวกเขาต้องการค้นหาเส้นทางของไฟล์ปฏิบัติการใน C ++? ฉันหวังว่าจะมีบางอย่างที่ฟังดูเรียบง่ายเช่นนี้แล้วจะถูกนำไปใช้ในไลบรารีเช่นบูสต์
Ben Hymers

2
@curiousguy ฉันไม่แน่ใจว่าฉันเข้าใจคุณ; ฉันค่อนข้างมั่นใจว่านั่นคือประเด็นทั้งหมดของคำถามนี้ :)
Ben Hymers

6
@curiousguy: คุณต้องการทำเช่นนี้หากโปรแกรมของคุณอาจติดตั้งในไดเร็กทอรีที่ผู้ใช้เลือก คุณต้องสามารถค้นหาไฟล์ปฏิบัติการและไฟล์สนับสนุนได้
greyfade

1
@ เป็ดคุณจะอัปเดตคำตอบของคุณพร้อมลิงก์ไปยัง lib ของฉันหรือไม่? ความคิดเห็นของฉันถูกฝังอยู่ในรายการ
Gregory Pakosz

35

ฟังก์ชันboost :: dll :: program_locationเป็นหนึ่งในวิธีการข้ามแพลตฟอร์มที่ดีที่สุดในการรับเส้นทางของไฟล์ปฏิบัติการที่กำลังทำงานอยู่ที่ฉันรู้จัก ไลบรารี DLL ถูกเพิ่มใน Boost ในเวอร์ชัน 1.61.0

ต่อไปนี้เป็นวิธีแก้ปัญหาของฉัน ฉันได้ทดสอบบน Windows, Mac OS X, Solaris, Free BSD และ GNU / Linux

ต้องมีBoost 1.55.0 ขึ้นไป ใช้ไลบรารี Boost.FilesystemโดยตรงและBoost.Localeห้องสมุดและBoost.Systemห้องสมุดทางอ้อม

src / executable_path.cpp

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>

#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
#  include <boost/process.hpp>
#endif

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
#  include <Windows.h>
#endif

#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>

namespace boost {

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector buf(1024, 0);
  size_type size = buf.size();
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    DWORD result = GetModuleFileNameA(nullptr, &buf[0], size);
    DWORD lastError = GetLastError();
    if (result == 0)
    {
      shouldContinue = false;
    }
    else if (result < size)
    {
      havePath = true;
      shouldContinue = false;
    }
    else if (
      result == size
      && (lastError == ERROR_INSUFFICIENT_BUFFER || lastError == ERROR_SUCCESS)
      )
    {
      size *= 2;
      buf.resize(size);
    }
    else
    {
      shouldContinue = false;
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  // On Microsoft Windows, there is no need to call boost::filesystem::canonical or
  // boost::filesystem::path::make_preferred. The path returned by GetModuleFileNameA
  // is the one we want.
  std::string ret = &buf[0];
  return ret;
}

#elif (BOOST_OS_MACOS)

#  include <mach-o/dyld.h>

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  char_vector buf(1024, 0);
  uint32_t size = static_cast<uint32_t>(buf.size());
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    int result = _NSGetExecutablePath(&buf[0], &size);
    if (result == -1)
    {
      buf.resize(size + 1);
      std::fill(std::begin(buf), std::end(buf), 0);
    }
    else
    {
      shouldContinue = false;
      if (buf.at(0) != 0)
      {
        havePath = true;
      }
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#elif (BOOST_OS_SOLARIS)

#  include <stdlib.h>

std::string executable_path(const char* argv0)
{
  std::string ret = getexecname();
  if (ret.empty())
  {
    return detail::executable_path_fallback(argv0);
  }
  boost::filesystem::path p(ret);
  if (!p.has_root_directory())
  {
    boost::system::error_code ec;
    p = boost::filesystem::canonical(
      p, boost::filesystem::current_path(), ec);
    if (ec.value() != boost::system::errc::success)
    {
      return detail::executable_path_fallback(argv0);
    }
    ret = p.make_preferred().string();
  }
  return ret;
}

#elif (BOOST_OS_BSD)

#  include <sys/sysctl.h>

std::string executable_path(const char* argv0)
{
  typedef std::vector<char> char_vector;
  int mib[4]{0};
  size_t size;
  mib[0] = CTL_KERN;
  mib[1] = KERN_PROC;
  mib[2] = KERN_PROC_PATHNAME;
  mib[3] = -1;
  int result = sysctl(mib, 4, nullptr, &size, nullptr, 0);
  if (-1 == result)
  {
    return detail::executable_path_fallback(argv0);
  }
  char_vector buf(size + 1, 0);
  result = sysctl(mib, 4, &buf[0], &size, nullptr, 0);
  if (-1 == result)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#elif (BOOST_OS_LINUX)

#  include <unistd.h>

std::string executable_path(const char *argv0)
{
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector buf(1024, 0);
  size_type size = buf.size();
  bool havePath = false;
  bool shouldContinue = true;
  do
  {
    ssize_t result = readlink("/proc/self/exe", &buf[0], size);
    if (result < 0)
    {
      shouldContinue = false;
    }
    else if (static_cast<size_type>(result) < size)
    {
      havePath = true;
      shouldContinue = false;
      size = result;
    }
    else
    {
      size *= 2;
      buf.resize(size);
      std::fill(std::begin(buf), std::end(buf), 0);
    }
  } while (shouldContinue);
  if (!havePath)
  {
    return detail::executable_path_fallback(argv0);
  }
  std::string path(&buf[0], size);
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      path, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    return p.make_preferred().string();
  }
  return detail::executable_path_fallback(argv0);
}

#else

std::string executable_path(const char *argv0)
{
  return detail::executable_path_fallback(argv0);
}

#endif

}

src / รายละเอียด / executable_path_internals.cpp

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/predef.h>
#include <boost/version.hpp>
#include <boost/tokenizer.hpp>

#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
#  include <boost/process.hpp>
#endif

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)
#  include <Windows.h>
#endif

#include <boost/executable_path.hpp>
#include <boost/detail/executable_path_internals.hpp>

namespace boost {
namespace detail {

std::string GetEnv(const std::string& varName)
{
  if (varName.empty()) return "";
#if (BOOST_OS_BSD || BOOST_OS_CYGWIN || BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_SOLARIS)
  char* value = std::getenv(varName.c_str());
  if (!value) return "";
  return value;
#elif (BOOST_OS_WINDOWS)
  typedef std::vector<char> char_vector;
  typedef std::vector<char>::size_type size_type;
  char_vector value(8192, 0);
  size_type size = value.size();
  bool haveValue = false;
  bool shouldContinue = true;
  do
  {
    DWORD result = GetEnvironmentVariableA(varName.c_str(), &value[0], size);
    if (result == 0)
    {
      shouldContinue = false;
    }
    else if (result < size)
    {
      haveValue = true;
      shouldContinue = false;
    }
    else
    {
      size *= 2;
      value.resize(size);
    }
  } while (shouldContinue);
  std::string ret;
  if (haveValue)
  {
    ret = &value[0];
  }
  return ret;
#else
  return "";
#endif
}

bool GetDirectoryListFromDelimitedString(
  const std::string& str,
  std::vector<std::string>& dirs)
{
  typedef boost::char_separator<char> char_separator_type;
  typedef boost::tokenizer<
    boost::char_separator<char>, std::string::const_iterator,
    std::string> tokenizer_type;
  dirs.clear();
  if (str.empty())
  {
    return false;
  }
#if (BOOST_OS_WINDOWS)
  const std::string os_pathsep(";");
#else
  const std::string os_pathsep(":");
#endif
  char_separator_type pathSep(os_pathsep.c_str());
  tokenizer_type strTok(str, pathSep);
  typename tokenizer_type::iterator strIt;
  typename tokenizer_type::iterator strEndIt = strTok.end();
  for (strIt = strTok.begin(); strIt != strEndIt; ++strIt)
  {
    dirs.push_back(*strIt);
  }
  if (dirs.empty())
  {
    return false;
  }
  return true;
}

std::string search_path(const std::string& file)
{
  if (file.empty()) return "";
  std::string ret;
#if (BOOST_VERSION > BOOST_VERSION_NUMBER(1,64,0))
  {
    namespace bp = boost::process;
    boost::filesystem::path p = bp::search_path(file);
    ret = p.make_preferred().string();
  }
#endif
  if (!ret.empty()) return ret;
  // Drat! I have to do it the hard way.
  std::string pathEnvVar = GetEnv("PATH");
  if (pathEnvVar.empty()) return "";
  std::vector<std::string> pathDirs;
  bool getDirList = GetDirectoryListFromDelimitedString(pathEnvVar, pathDirs);
  if (!getDirList) return "";
  std::vector<std::string>::const_iterator it = pathDirs.cbegin();
  std::vector<std::string>::const_iterator itEnd = pathDirs.cend();
  for ( ; it != itEnd; ++it)
  {
    boost::filesystem::path p(*it);
    p /= file;
    if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
    {
      return p.make_preferred().string();
    }
  }
  return "";
}

std::string executable_path_fallback(const char *argv0)
{
  if (argv0 == nullptr) return "";
  if (argv0[0] == 0) return "";
#if (BOOST_OS_WINDOWS)
  const std::string os_sep("\\");
#else
  const std::string os_sep("/");
#endif
  if (strstr(argv0, os_sep.c_str()) != nullptr)
  {
    boost::system::error_code ec;
    boost::filesystem::path p(
      boost::filesystem::canonical(
        argv0, boost::filesystem::current_path(), ec));
    if (ec.value() == boost::system::errc::success)
    {
      return p.make_preferred().string();
    }
  }
  std::string ret = search_path(argv0);
  if (!ret.empty())
  {
    return ret;
  }
  boost::system::error_code ec;
  boost::filesystem::path p(
    boost::filesystem::canonical(
      argv0, boost::filesystem::current_path(), ec));
  if (ec.value() == boost::system::errc::success)
  {
    ret = p.make_preferred().string();
  }
  return ret;
}

}
}

รวม / เพิ่ม / executable_path.hpp

#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_

#pragma once

#include <string>

namespace boost {
std::string executable_path(const char * argv0);
}

#endif // BOOST_EXECUTABLE_PATH_HPP_

รวม / เพิ่ม / รายละเอียด / executable_path_internals.hpp

#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

#pragma once

#include <string>
#include <vector>

namespace boost {
namespace detail {
std::string GetEnv(const std::string& varName);
bool GetDirectoryListFromDelimitedString(
    const std::string& str,
    std::vector<std::string>& dirs);
std::string search_path(const std::string& file);
std::string executable_path_fallback(const char * argv0);
}
}

#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

ผมมีโครงการที่สมบูรณ์รวมทั้งการประยุกต์ใช้การทดสอบและการ CMake สร้างไฟล์ที่มีอยู่ที่SnKOpen - / เวอร์ชันนี้สมบูรณ์กว่าเวอร์ชันที่ฉันให้ไว้ที่นี่ นอกจากนี้ยังรองรับแพลตฟอร์มเพิ่มเติม

ฉันได้ทดสอบแอปพลิเคชันบนระบบปฏิบัติการที่รองรับทั้งหมดในสี่สถานการณ์ต่อไปนี้

  1. พา ธ สัมพัทธ์ซึ่งเรียกใช้งานได้ในไดเร็กทอรีปัจจุบัน: เช่น ./executable_path_test
  2. พา ธ สัมพัทธ์ซึ่งเรียกใช้งานได้ในไดเร็กทอรีอื่น: เช่น ./build/executable_path_test
  3. เส้นทางแบบเต็ม: เช่น / some / dir /ecuteable_path_test
  4. เรียกใช้งานได้ในพา ธ ชื่อไฟล์เท่านั้น: เช่น executable_path_test

ในทั้งสี่สถานการณ์ทั้งฟังก์ชัน executable_path และ executable_path_fallback จะทำงานและส่งกลับผลลัพธ์เดียวกัน

หมายเหตุ

นี่คือคำตอบที่อัปเดตสำหรับคำถามนี้ ฉันอัปเดตคำตอบเพื่อพิจารณาความคิดเห็นและข้อเสนอแนะของผู้ใช้ ฉันยังเพิ่มลิงค์ไปยังโปรเจ็กต์ใน SVN Repository ของฉัน


1
ดูเหมือนว่าจะเป็นโซลูชันที่สมบูรณ์พร้อมด้วยทางเลือกที่สมเหตุสมผล +1! คำถามหนึ่งที่ว่า: การแทนที่บัฟเฟอร์ char [1024] คงที่ด้วยบางอย่างเช่นเวกเตอร์ <char> ที่สามารถปรับขนาดได้จะเหมาะสมหรือไม่หากพา ธ เกินขนาดเริ่มต้น
Daniel Wolf

ใช่. นั่นคือข้อเสนอแนะที่ยอดเยี่ยม แน่นอนว่าจะต้องมีการเปลี่ยนแปลงเพิ่มเติมบางอย่างเช่นการตรวจสอบข้อผิดพลาดการปรับขนาดบัฟเฟอร์และลองอีกครั้ง
Ben Key

1
ฉันคิดว่าทางเลือกไม่ถูกต้อง argv[0]ยังสามารถเป็นเพียงชื่อเรียกใช้งานได้ซึ่งในกรณีนี้จำเป็นต้องค้นหาในPATHระบบ * nix
MichałGórny

1
ฉันลองใช้สิ่งนี้ แต่มันต้องการการเพิ่มใช่มั้ย? ฉันคิดว่ามันเป็นแบบสแตนด์อโลน
manatttta

1
คุณมีฉันที่ "boost :: dll :: program_location"
Thomas

31

วิธีนี้ใช้ 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()


stem ดูเหมือนจะให้ฉันเป็นเพียงแค่ไฟล์ปฏิบัติการลบพา ธ และส่วนขยายบน Windows แต่นั่นเป็นจุดเล็กน้อย สิ่งที่ฉันต้องการทราบคือวิธีการทำงานเมื่อ argv [0] อาจไม่ถูกต้อง? มันใช้งานได้สำหรับฉันในการทดสอบบน Windows แต่แล้ว argv [0] ก็ถูกส่งต่อเป็นพา ธ สัมบูรณ์ของไฟล์ปฏิบัติการซึ่งทำให้งาน system_complete ค่อนข้างง่าย :)
Ben Hymers

1
ไม่ - เขาไม่ต้องการไดเร็กทอรีการทำงาน และไม่มี argv ไม่ช่วย คุณจะทำอย่างไรเมื่อ argv มีเฉพาะชื่อที่เรียกใช้งานได้? จะทำอย่างไรเมื่อเรียกใช้โปรแกรมผ่าน symlink?
Ichthyo

4
"// ไม่มีชื่อไฟล์" - คุณต้องการ.parent_path()ไม่ใช่.stem()หรือ
Claudiu

2
ดูเหมือนจะใช้ไม่ได้บนแพลตฟอร์มของฉัน (macOS El Capitan) ฉันได้รับไดเร็กทอรีการทำงานปัจจุบันแทน ก็อย่างที่@Claudiuบอกว่าน่าจะ.parent_path()ใช่
samvv

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

3
โปรดทราบว่าต้องใช้WCHAR ownPth..โดยพันรอบ a #ifdef UNICODEในกรณีที่คอมไพล์ด้วยการสนับสนุน Unicode หากไม่มีให้ใช้รหัสที่ให้มา
Dr1Ku

1
สำหรับเร็กคอร์ดฉันแค่มีกรณีตลก ๆ ที่ GetModuleDirectory ส่งกลับเส้นทางที่มีส่วน ".. " อยู่ในนั้นเช่นถ้ามันใช้สตริงที่บริสุทธิ์ดิบจากบรรทัดคำสั่งฮ่า ๆ ในกรณีนี้ Visual Studio กำลังเปิดตัวกระบวนการและ .. เป็นส่วนหนึ่งของเส้นทางการดีบัก บางอย่างเช่น $ (projectDir) ../ some.exe ฉันใช้ PathCanonicalize จาก Shwlib แต่จำเป็นต้องเชื่อมโยงกับ lib นี้ สิ่งนี้อาจไม่เป็นที่ต้องการ
v.oddou

1
ฉันขอแนะนำให้ใช้ TCHAR สำหรับ ownPath แทน char แต่คำตอบที่ดีอย่างไรก็ตาม
anhoppe

เป็นไปได้หรือไม่ที่สิ่งนี้จะล้มเหลว? ดูไม่น่าจะเป็นไปได้อย่างรวดเร็ว ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
หากพารามิเตอร์แรกสำหรับ GetModuleFileName เป็น NULL พารามิเตอร์จะดึงเส้นทางของไฟล์ปฏิบัติการของกระบวนการปัจจุบัน
lsalamon

12

สำหรับ Windows:

GetModuleFileName - ส่งคืนเส้นทาง exe + ชื่อไฟล์ exe

เพื่อลบชื่อไฟล์
PathRemoveFileSpec


1
เอกสารทราบสำหรับ This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its placePathRemoveFileSpec:
javs

12

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

สงสัยว่าโซลูชันนี้น่าจะพกพาได้ แต่ไม่รู้ว่าจะใช้ Unicode บน OS อื่นอย่างไร

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

ยามภายในส่วนหัวไม่ได้ทำการทดสอบที่เหมาะสมสำหรับการมีอยู่ของระบบไฟล์ cppreference แสดงให้เห็นว่าค่าของแมโครทดสอบคุณลักษณะถูกกำหนดไว้ในส่วนหัวของระบบไฟล์ดังนั้นจึงไม่สามารถทดสอบก่อนที่จะรวม __has_include () เป็นการทดสอบมาตรฐานที่ดีกว่าที่นี่
Meteorhead

8

QT ให้สิ่งนี้กับ OS Abstraction เป็นQCoreApplication :: applicationDirPath


การเดินทางด้วยสิ่งนี้: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. มีความคิดอย่างไรที่จะแก้ปัญหานั้น?
GuySoft

@GuySoft: เพียงแค่สร้างอินสแตนซ์QCoreApplicationเช่นนั้นQApplication application(argc, argv);(ทำในของคุณmain(argc, argv)และต้องแน่ใจว่าคุณไม่ได้แก้ไขargc/argvเนื่องจากสิ่งเหล่านี้จำเป็นต้องใช้งานได้ตลอดอายุการใช้งานของ QCoreApplication (ตรวจสอบเอกสารประกอบ )
ted

5

นี่เป็นวิธีเฉพาะของ 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);

ฉันขอแนะนำให้ใช้การตรวจจับแพลตฟอร์มเป็นคำสั่งก่อนตัวประมวลผลเพื่อเปลี่ยนการใช้งานฟังก์ชัน Wrapper ที่เรียกใช้GetThisPathสำหรับแต่ละแพลตฟอร์ม


3

ใช้ 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 ดังนั้นผลลัพธ์จึงถูกต้อง


จะเกิดอะไรขึ้นถ้า '/' ไม่ปรากฏในเส้นทาง? ไม่มีการตรวจสอบกรณีนั้นและฉันเชื่อว่ามันค่อนข้างเป็นไปได้ - Windows จะใช้แบ็กสแลชและargs[0]อาจไม่ใช่เส้นทางเลย
Ben Hymers

ถ้าไม่มี '/' rfind return -1 ดังนั้น "path" = aux.substr (0,0) และ "name" = aux.substr (0): ผลลัพธ์ถูกต้อง เกี่ยวข้องกับ Windows คุณพูดถูกต้อง '/' ต้องเปลี่ยนเป็น '\\' ฉันจะเปลี่ยนเป็นอนุญาต windows ด้วย ฉันได้ทดสอบชื่อไฟล์ด้วย '/' ด้วย แต่สุดท้ายนี้ได้รับการเข้ารหัสและไม่สร้างปัญหา
Adrian Maire

1
มันเป็นส่วนที่args[0]ไม่จำเป็นต้องเป็นเส้นทางปฏิบัติการที่รบกวนฉัน ขอบคุณสำหรับการแก้ไขคำตอบสำหรับ Windows :)
Ben Hymers

1
ถ้าคำสั่งถูกรันโดยไม่ให้พา ธ (กล่าวคือพบโดยอยู่ในไดเร็กทอรีที่กำหนดใน PATH env var) args [0] จะเป็นเพียงชื่อของไฟล์ที่เรียกใช้งานได้โดยไม่มีพา ธ
Kevin

@ เควิน: คุณ (และคนอื่น ๆ ) พูดถูกนี่เป็นวิธีง่ายๆสำหรับเครื่องมือเล็ก ๆ น้อย ๆ ที่ใช้งานได้ถึง 95% ของเคส สำหรับซอฟต์แวร์ที่ร้ายแรงไฟล์คอนฟิกูเรชันและ / หรือตัวแปรสภาพแวดล้อมน่าจะดีกว่า นอกจากนี้ความต้องการนี้มักจะบ่งบอกถึงการออกแบบที่ไม่ค่อยดี (หรือผิดพลาด)
Adrian Maire


1

ต่อไปนี้เป็นวิธีแก้ปัญหาที่รวดเร็วและสกปรก แต่โปรดทราบว่ายังห่างไกลจากการป้องกันการเข้าใจผิด

#include <iostream>

using namespace std ;

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

17
ฉันเคยเห็นคำถาม SO อื่น ๆ ว่าสิ่งนี้ใช้ไม่ได้เสมอไปและ argv [0] สามารถมีพา ธ สัมบูรณ์ไปยังไฟล์ปฏิบัติการได้เพียงแค่ชื่อไฟล์ของไฟล์ปฏิบัติการหรือขยะอื่น ๆ
Ben Hymers

7
เราไม่ควรเชื่อถือ argv [0] หากพวกเขาพยายามเปิด 'ไฟล์สนับสนุน' หรือสิ่งที่คล้ายกัน Argv อาจมีการเปลี่ยนแปลงและผู้โทรที่ชั่วร้ายสามารถเปลี่ยนค่าของสิ่งนี้ได้ หลีกเลี่ยงเว้นแต่คุณจะใช้เพื่อบันทึก ฯลฯ ไม่ใช่สำหรับการสร้างเส้นทางที่ใช้เปิดไฟล์
Qix - MONICA MISTREATED

สิ่งนี้ใช้ไม่ได้กับ Windows argv [0] จะไม่มีเส้นทางแบบเต็ม เฉพาะไฟล์. exe โปรดอย่าลองใช้ bash shell ลองในคอนโซลมาตรฐานและ cout << argv [0] เพื่อทำซ้ำ
Freddy Martinez Garcia

@FreddyMartinezGarcia ฉันจะทดสอบใน Windows ดังนั้น YMMV มันคืออะไรก็ตามที่ใช้ในการเปิดโค้ด หากคุณปฏิบัติการใน CWD แน่ใจว่าคุณจะได้รับชื่อไฟล์เท่านั้น
Clifford

0

ในกรณีที่คุณต้องการจัดการพา ธ Unicode สำหรับ 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;
}

0

สำหรับ Windows คุณมีปัญหาในการถอดไฟล์ปฏิบัติการออกจากผลลัพธ์ของGetModuleFileName()ไฟล์. การเรียก Windows API PathRemoveFileSpec()ที่ Nate ใช้เพื่อจุดประสงค์นั้นในคำตอบของเขาเปลี่ยนไประหว่าง 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.
}

0

ดังที่คนอื่น ๆ กล่าวถึงargv[0]เป็นวิธีแก้ปัญหาที่ดีหากแพลตฟอร์มนั้นผ่านเส้นทางปฏิบัติการจริงซึ่งมีความเป็นไปได้ไม่น้อยกว่าระบบปฏิบัติการที่เป็น Windows (โดยที่ WinAPI สามารถช่วยค้นหาเส้นทางที่ปฏิบัติการได้) หากคุณต้องการตัดสตริงเพื่อรวมเฉพาะพา ธ ไปยังไดเร็กทอรีที่มีไฟล์ปฏิบัติการอยู่ให้ใช้พา ธ นั้นเพื่อค้นหาไฟล์แอพพลิเคชั่นอื่น ๆ (เช่นเนื้อหาเกมหากโปรแกรมของคุณเป็นเกม) ก็ทำได้ดีเนื่องจากการเปิดไฟล์นั้นสัมพันธ์กับ ไดเร็กทอรีการทำงานหรือรูทหากมีให้


0

นี่คือสิ่งที่ฉันลงเอยด้วย

ไฟล์ส่วนหัวมีลักษณะดังนี้:

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

}

0

ห้องสมุด SDL2 ( https://www.libsdl.org/ ) มีสองฟังก์ชั่นที่ใช้งานได้ในหลากหลายแพลตฟอร์ม:

  • SDL_GetBasePath
  • SDL_GetPrefPath

ดังนั้นหากคุณไม่ต้องการสร้างวงล้อขึ้นมาใหม่ ... น่าเสียดายที่หมายความรวมถึงไลบรารีทั้งหมดแม้ว่าจะมีใบอนุญาตที่ค่อนข้างอนุญาตและก็สามารถคัดลอกรหัสได้ นอกจากนี้ยังมีฟังก์ชันข้ามแพลตฟอร์มอื่น ๆ อีกมากมาย


0

นี่อาจเป็นวิธีที่เป็นธรรมชาติที่สุดในขณะที่ครอบคลุมแพลตฟอร์มเดสก์ท็อปหลัก ๆ ส่วนใหญ่ ฉันไม่แน่ใจ แต่ฉันเชื่อว่าสิ่งนี้ควรใช้ได้กับ BSD ทั้งหมดไม่ใช่แค่ FreeBSD หากคุณเปลี่ยนการตรวจสอบมาโครแพลตฟอร์มเพื่อให้ครอบคลุมทั้งหมด ถ้าฉันเคยติดตั้ง Solaris ฉันจะเพิ่มแพลตฟอร์มนั้นในรายการที่รองรับ

มีการสนับสนุน UTF-8 เต็มรูปแบบบน Windows ซึ่งทุกคนไม่สนใจพอที่จะไปได้ไกลขนาดนั้น

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

สิ่งนี้ช่วยให้ได้รับเส้นทางแบบเต็มไปยังการเรียกใช้งานรหัสกระบวนการใด ๆ ยกเว้นใน Windows มีกระบวนการบางอย่างที่มีคุณลักษณะด้านความปลอดภัยซึ่งจะไม่อนุญาตดังนั้น wysiwyg โซลูชันนี้จึงไม่สมบูรณ์แบบ

เพื่อตอบคำถามที่ถามอย่างแม่นยำยิ่งขึ้นคุณสามารถทำได้:

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

ดู Readme สำหรับรายการฟังก์ชันส่วนใหญ่ที่รวมอยู่


0

หากใช้ C ++ 17 คุณสามารถทำสิ่งต่อไปนี้เพื่อรับเส้นทางไปยังไฟล์ปฏิบัติการ

#include <filesystem>

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

คำตอบข้างต้นได้รับการทดสอบบน Debian 10 โดยใช้ G ++ 9.3.0


โปรดทราบว่าสิ่งนี้จะใช้ได้ก็ต่อเมื่อมี / proc / self / exe อยู่และสามารถเข้าถึงได้ คุณควรตรวจสอบว่าเป็นกรณีนี้หรือไม่
Zrin

-1

ณ C ++ 17:

ตรวจสอบให้แน่ใจว่าคุณรวมระบบไฟล์ std

#include <filesystem>

และตอนนี้คุณสามารถทำได้

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

เพิ่มระบบไฟล์กลายเป็นส่วนหนึ่งของ lib มาตรฐาน

ถ้าหาไม่เจอลองดูข้างล่าง:

std::experimental::filesystem

10
นี่ไม่ใช่เส้นทางของไบนารี แต่เป็นไดเร็กทอรีการทำงานปัจจุบัน
Zitrax

-2

นี่คือทางออกของฉันใน 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());
    }
}

เหตุผลในการใช้คืออะไรnewและ (ผิด) delete? หากคุณใช้ a std::vectorรหัสของคุณจะไม่แสดงพฤติกรรมที่ไม่ได้กำหนดไว้
IInspectable

นอกจากนี้GetModuleFileNameWอย่าตั้งรหัสข้อผิดพลาดสุดท้ายในกรณีที่ทำสำเร็จ รหัสนั้นเสียในหลาย ๆ ด้าน อย่าใช้ถ้าคุณบังเอิญเจอสิ่งนี้
IInspectable

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

2
นั่นคือ Windows เท่านั้นและใช้ MFC ซึ่งค่อนข้างห่างไกลจากการข้ามแพลตฟอร์มขออภัย!
Ben Hymers

1
นี่ไม่ใช่วิธีการของ Windows ด้วยเช่นกัน ลองดูPathRemoveFileSpec()และฟังก์ชั่นที่เกี่ยวข้องแทน
Remy Lebeau

-4

ใน Unix (รวมถึง Linux) ให้ลอง 'ซึ่ง' ใน Windows ให้ลอง 'where'

#include <stdio.h>

#define _UNIX

int main(int argc, char** argv)
{
        char cmd[128];
        char buf[128];
        FILE* fp = NULL;
#if defined(_UNIX)
        sprintf(cmd, "which %s > my.path", argv[0]);
#else
        sprintf(cmd, "where %s > my.path", argv[0]);
#endif
        system(cmd);
        fp = fopen("my.path", "r");
        fgets(buf, sizeof(buf), fp);
        fclose(fp);

        printf("full path: %s\n", buf);
        unlink("my.path");

        return 0;
}

-4

วิธีนี้ใช้ได้กับทั้ง Windows และ 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;
}

2
สิ่งนี้ส่งคืนไดเร็กทอรีการทำงานปัจจุบันไม่ใช่พา ธ ไปยังไฟล์ปฏิบัติการซึ่งอาจไม่ใช่สิ่งเดียวกัน
Dave Durbin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.