Dapatkan jalur yang dapat dieksekusi


114

Saya tahu pertanyaan ini telah ditanyakan sebelumnya tetapi saya masih belum melihat jawaban yang memuaskan, atau jawaban yang pasti "tidak, ini tidak dapat dilakukan", jadi saya akan bertanya lagi!

Yang ingin saya lakukan adalah mendapatkan jalur ke eksekusi yang sedang berjalan, baik sebagai jalur absolut atau relatif ke tempat eksekusi dipanggil, dengan cara yang tidak bergantung platform. Saya pikir boost :: filesystem :: initial_path adalah jawaban untuk masalah saya tetapi tampaknya hanya menangani bagian 'platform-independen' dari pertanyaan - ia masih mengembalikan jalur tempat aplikasi dipanggil.

Untuk sedikit latar belakang, ini adalah game yang menggunakan Ogre, yang saya coba buat profilnya menggunakan Very Sleepy, yang menjalankan target yang dapat dieksekusi dari direktorinya sendiri, jadi tentu saja saat memuat game tidak menemukan file konfigurasi, dll. Dan segera crash . Saya ingin dapat memberikan jalur absolut ke file konfigurasi, yang saya tahu akan selalu ada di samping file yang dapat dieksekusi. Hal yang sama berlaku untuk debugging di Visual Studio - Saya ingin dapat menjalankan $ (TargetPath) tanpa harus mengatur direktori kerja.



9
Perhatikan bahwa tidak mungkin untuk membuktikan ketiadaan jawaban, oleh karena itu Anda tidak bisa mendapatkan TIDAK yang pasti . Saya akan dengan senang hati memberi Anda TIDAK otoritatif :)
MSalters

kemungkinan duplikat dari cara menemukan lokasi eksekusi di C
ergosys

" saat memuat, game tidak menemukan file konfigurasi, dll. " jadi game mencari file konfigurasi di direktori saat ini? Itu ide yang buruk, dan berpotensi merupakan kerentanan keamanan. File konfigurasi harus disimpan di lokasi standar.
penasaran

1
Saya memposting jawaban di sini untuk pertanyaan terkait yang juga menjawab pertanyaan Anda, bekerja di berbagai platform menggunakan boost
jtbr

Jawaban:


86

Tidak ada cara lintas platform yang saya tahu.

Untuk Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
Kemandirian platform hanyalah masalah menyembunyikan ketergantungan platform. Dalam kasus ini, menggunakan makro OS standar yang dirinci di predef.sourceforge.net/preos.html untuk memilih metode ini sangatlah mudah.
Clifford

4
Jadi, apakah ini yang dilakukan setiap orang setiap kali mereka ingin menemukan jalur yang dapat dieksekusi di C ++? Saya berharap sesuatu yang terdengar sederhana seperti ini sudah diterapkan di perpustakaan seperti boost.
Ben Hymers

2
@curiousguy Saya tidak yakin saya mengerti Anda; Saya cukup yakin itulah inti dari pertanyaan ini :)
Ben Hymers

6
@curiousguy: Anda ingin melakukannya jika, misalnya, program Anda mungkin terinstal di direktori yang dipilih pengguna. Anda harus dapat menemukan file yang dapat dieksekusi dan file pendukungnya .
greyfade

1
@Duck, maukah Anda memperbarui jawaban Anda dengan tautan ke lib saya? Komentar saya terkubur dalam daftar.
Gregory Pakosz

35

Fungsi boost :: dll :: program_location adalah salah satu metode lintas platform terbaik untuk mendapatkan jalur eksekusi yang dapat dijalankan yang saya ketahui. Pustaka DLL telah ditambahkan ke Boost dalam versi 1.61.0.

Berikut ini adalah solusi saya. Saya telah mengujinya di Windows, Mac OS X, Solaris, Free BSD, dan GNU / Linux.

Ini membutuhkan Boost 1.55.0 atau lebih tinggi. Ia menggunakan perpustakaan Boost.Filesystem langsung dan Boost.Locale perpustakaan dan Boost.System perpustakaan secara tidak langsung.

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

}
}

sertakan / 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_

sertakan / 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_

Saya memiliki proyek yang lengkap, termasuk aplikasi pengujian dan file build CMake yang tersedia di SnKOpen - / cpp / executable_path / trunk . Versi ini lebih lengkap dari versi yang saya berikan di sini. Ini juga mendukung lebih banyak platform.

Saya telah menguji aplikasi pada semua sistem operasi yang didukung dalam empat skenario berikut.

  1. Jalur relatif, dapat dieksekusi di direktori saat ini: mis ./executable_path_test
  2. Jalur relatif, dapat dieksekusi di direktori lain: yaitu ./build/executable_path_test
  3. Path lengkap: ie / some / dir / executable_path_test
  4. Dapat dijalankan di jalur, hanya nama file: yaitu executable_path_test

Dalam keempat skenario, fungsi executable_path dan executable_path_fallback berfungsi dan mengembalikan hasil yang sama.

Catatan

Ini adalah jawaban terbaru untuk pertanyaan ini. Saya memperbarui jawaban untuk mempertimbangkan komentar dan saran pengguna. Saya juga menambahkan link ke sebuah proyek di SVN Repository saya.


1
Itu terlihat seperti solusi yang sangat lengkap dengan fallback yang masuk akal. +1! Namun, satu pertanyaan: Apakah masuk akal untuk mengganti buffer char [1024] tetap dengan sesuatu seperti vektor <char> yang dapat diubah ukurannya jika jalurnya melebihi ukuran awal?
Daniel Wolf

Iya. Itu adalah saran yang sangat bagus. Tentu saja beberapa perubahan tambahan perlu dilakukan seperti memeriksa kesalahan, mengubah ukuran buffer, dan mencoba lagi.
Ben Key

1
Menurut saya, fallback tersebut salah. argv[0]bisa juga berupa nama yang dapat dieksekusi, dalam hal ini perlu mencarinya di PATHsistem * nix.
Michał Górny

1
Saya mencoba menggunakan ini. tapi butuh dorongan, benar? Saya pikir itu mandiri
manatttta

1
Anda memiliki saya di "boost :: dll :: program_location"
Thomas

31

Cara ini menggunakan boost + argv. Anda menyebutkan ini mungkin bukan lintas platform karena mungkin atau mungkin tidak termasuk nama yang dapat dieksekusi. Nah kode berikut harus mengatasi itu.

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

Kode berikut mendapatkan direktori kerja saat ini yang dapat melakukan apa yang Anda butuhkan

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

Catatan Baru menyadari bahwa basename() sudah usang jadi harus beralih ke.stem()


stem tampaknya memberi saya hanya yang dapat dieksekusi dikurangi jalur dan ekstensi pada Windows, tapi itu poin kecil. Yang ingin saya ketahui adalah bagaimana ini bekerja di sekitar argv [0] yang mungkin salah? Ini berfungsi untuk saya pengujian di Windows, tetapi kemudian argv [0] sebenarnya sedang diteruskan sebagai jalur absolut dari eksekusi, yang membuat pekerjaan system_complete cukup mudah :)
Ben Hymers

1
Tidak - dia tidak membutuhkan direktori kerja. dan NO argv tidak membantu. Apa yang Anda lakukan jika argv hanya berisi nama yang dapat dieksekusi? Apa yang harus dilakukan, ketika program dipanggil melalui symlink?
Ichthyo

4
"// Tanpa nama file" - kamu mau .parent_path(), bukan .stem()?
Claudiu

2
Ini sepertinya tidak berfungsi di platform saya (macOS El Capitan). Saya mendapatkan direktori kerja saat ini sebagai gantinya. Juga, seperti yang @Claudiudikatakan, saya pikir seharusnya begitu .parent_path().
samvv

20

Saya tidak yakin tentang Linux, tetapi coba ini untuk 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
Perhatikan bahwa seseorang harus menggunakan WCHAR ownPth.., melingkari a #ifdef UNICODEjika dikompilasi dengan dukungan Unicode. Jika tidak, gunakan kode yang tersedia.
Dr1Ku

1
hanya sebagai catatan saya hanya mengalami kasus lucu di mana GetModuleDirectory mengembalikan jalur dengan bagian ".." di dalamnya, seperti jika itu mengambil string murni mentah dari baris perintah lol. sebenarnya dalam kasus ini studio visual meluncurkan proses dan .. merupakan bagian dari jalur debugging. sesuatu seperti $ (projectDir) ../ some.exe Saya menggunakan PathCanonicalize dari Shwlib tetapi seseorang perlu menautkan ke lib ini. ini mungkin tidak diinginkan.
v.oddou

1
Saya juga merekomendasikan untuk menggunakan TCHAR untuk ownPath daripada char. Tapi jawaban yang bagus.
anhoppe

Apakah mungkin ini gagal? Sekilas sepertinya tidak mungkin ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
Jika parameter pertama untuk GetModuleFileName adalah NULL, itu mengambil jalur dari file yang dapat dieksekusi dari proses saat ini.
lsalamon

12

Untuk windows:

GetModuleFileName - mengembalikan jalur exe + nama file exe

Untuk menghapus nama file
PathRemoveFileSpec


1
Docs diperhatikan untuk PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs

12

C ++ 17, windows, unicode, menggunakan api baru sistem file:

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

Dugaan solusi ini harus portabel, tetapi tidak tahu bagaimana unicode diimplementasikan pada OS lain.

weakly_canonical diperlukan hanya jika Anda menggunakan sebagai referensi folder atas Direktori Output ('..') untuk menyederhanakan jalur. Jika Anda tidak menggunakannya - hapus.

Jika Anda beroperasi dari pustaka tautan dinamis (.dll /.so), maka Anda mungkin tidak memiliki argv, maka Anda dapat mempertimbangkan solusi berikut:

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
}

Penjaga di dalam header bukanlah pengujian yang tepat untuk keberadaan sistem file. cppreference menunjukkan bahwa nilai makro uji fitur ditentukan di tajuk sistem file itu sendiri, sehingga tidak berfungsi untuk menguji sebelum dimasukkan. __has_include () adalah pengujian standar yang lebih baik di sini.
Meteorhead

8

QT menyediakan ini dengan abstraksi OS sebagai QCoreApplication :: applicationDirPath ()


Mendapatkan dengan ini: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Ada ide bagaimana mengatasinya?
GuySoft

@GuySoft: Cukup buat contoh QCoreApplicationseperti itu QApplication application(argc, argv);(lakukan ini di Anda main(argc, argv), dan pastikan Anda tidak memodifikasi argc/argv, karena ini harus tetap valid selama masa pakai QCoreApplication (periksa dokumentasi )
ted

5

Ini adalah cara khusus Windows, tetapi setidaknya setengah dari jawaban Anda.

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

Saya akan menyarankan menggunakan deteksi platform sebagai arahan preprocessor untuk mengubah implementasi fungsi pembungkus yang memanggil GetThisPathsetiap platform.


3

Menggunakan args [0] dan mencari '/' (atau '\\'):

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

DIEDIT: Jika '/' tidak ada, pos == - 1 jadi hasilnya benar.


Bagaimana jika '/' tidak ada di jalur? Tidak ada pemeriksaan kasus itu dan saya yakin itu sangat mungkin - Windows akan menggunakan garis miring terbalik, dan args[0]mungkin sebenarnya bukan jalur sama sekali.
Ben Hymers

Jika '/' tidak ada, rfind return -1, jadi "path" = aux.substr (0,0) dan "name" = aux.substr (0): hasilnya benar. Terkait dengan Windows, Anda benar, '/' harus diubah menjadi '\\', saya juga akan mengubah untuk mengizinkan jendela. Saya juga telah menguji nama file dengan '/', tetapi yang terakhir ini dikodifikasikan dan tidak menimbulkan masalah.
Adrian Maire

1
Ini lebih merupakan bagian tentang args[0]tidak selalu menjadi jalur yang dapat dieksekusi yang mengganggu saya. Terima kasih telah memperbaiki jawaban Anda untuk Windows :)
Ben Hymers

1
Jika perintah dijalankan tanpa memberikan jalur (yaitu ditemukan dengan berada di direktori yang diberikan dalam PATH env var), args [0] hanya akan menjadi nama yang dapat dieksekusi, tanpa jalur.
Kevin

@Kevin: Anda (dan orang lain) benar, ini adalah solusi sederhana, untuk alat kecil, yang berfungsi ~ 95% kasus. Untuk perangkat lunak yang serius, file konfigurasi dan / atau variabel lingkungan mungkin lebih baik. Juga, kebutuhan ini biasanya menyiratkan desain yang tidak terlalu baik (atau bahkan salah).
Adrian Maire


1

Berikut ini berfungsi sebagai solusi cepat dan kotor, tetapi perlu diperhatikan bahwa ini jauh dari kata mudah:

#include <iostream>

using namespace std ;

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

17
Saya telah melihat pada pertanyaan SO lain bahwa ini tidak selalu berhasil, dan argv [0] dapat berisi path absolut ke executable, hanya nama file yang dapat dieksekusi, atau sampah lainnya.
Ben Hymers

7
Seseorang tidak boleh mempercayai argv [0] jika mereka mencoba untuk membuka 'file pendukung' atau sejenisnya. Argv dapat berubah, dan penelepon yang jahat dapat mengubah nilainya. Hindari kecuali Anda menggunakannya untuk logging, dll., BUKAN untuk membuat jalur yang digunakan untuk membuka file.
Qix - MONICA DISALAHKAN

ini tidak berfungsi di Windows. argv [0] tidak akan memiliki jalur lengkap. Hanya file .exe. Tolong, jangan mencoba di bash shell, coba di konsol standar dan cout << argv [0] untuk mereproduksi.
Freddy Martinez Garcia

@FreddyMartinezGarcia Yah saya akan mengujinya di Windows, jadi YMMV. Itu adalah apa pun yang digunakan untuk meluncurkan kode. Jika Anda dapat dieksekusi di CWD yakin Anda hanya akan mendapatkan nama file.
Clifford

0

Jika Anda perlu menangani jalur unicode untuk 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

Untuk Windows, Anda memiliki masalah tentang cara menghapus file yang dapat dieksekusi dari hasil GetModuleFileName(). Panggilan Windows API PathRemoveFileSpec()yang digunakan Nate untuk tujuan itu dalam jawabannya berubah antara Windows 8 dan pendahulunya. Jadi bagaimana agar tetap kompatibel dengan keduanya dan aman? Untungnya, ada C ++ 17 (atau Boost, jika Anda menggunakan kompiler lama). Saya melakukan ini:

#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

Seperti yang disebutkan orang lain, argv[0]ini adalah solusi yang cukup bagus, asalkan platform tersebut benar-benar melewati jalur yang dapat dieksekusi, yang tentunya tidak kurang mungkin daripada OS menjadi Windows (di mana WinAPI dapat membantu menemukan jalur yang dapat dieksekusi). Jika Anda ingin menghapus string untuk hanya menyertakan jalur ke direktori tempat eksekutabel berada, maka menggunakan jalur tersebut untuk menemukan file aplikasi lain (seperti aset game jika program Anda adalah game) tidak masalah, karena membuka file relatif terhadap direktori kerja, atau, jika tersedia, root.


0

Inilah yang akhirnya saya dapatkan

File header terlihat seperti ini:

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

}

Penerapan


#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

Perpustakaan SDL2 ( https://www.libsdl.org/ ) memiliki dua fungsi yang diterapkan di berbagai platform:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Jadi jika Anda tidak ingin menemukan kembali roda ... sayangnya, itu berarti menyertakan seluruh perpustakaan, meskipun memiliki lisensi yang cukup permisif dan seseorang juga dapat menyalin kodenya. Selain itu, ini menyediakan banyak fungsi lintas platform lainnya.


0

Ini mungkin cara paling alami untuk melakukannya, sambil mencakup sebagian besar platform desktop utama. Saya tidak yakin, tapi saya yakin ini harus bekerja dengan semua BSD, bukan hanya FreeBSD, jika Anda mengubah pemeriksaan makro platform untuk mencakup semuanya. Jika saya pernah menginstal Solaris, saya pasti akan menambahkan platform itu ke daftar yang didukung.

Menampilkan dukungan penuh UTF-8 di Windows, yang tidak semua orang cukup peduli untuk melangkah sejauh itu.

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

Hal ini memungkinkan mendapatkan path lengkap ke eksekusi dari hampir semua id proses, kecuali pada Windows ada beberapa proses dengan atribut keamanan yang tidak mengizinkannya, jadi wysiwyg, solusi ini tidak sempurna.

Untuk menjawab pertanyaan yang diajukan dengan lebih tepat, Anda dapat melakukan ini:

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

Bangun struktur file di atas dengan perintah ini:

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

Untuk mengunduh salinan file yang tercantum di atas:

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

Untuk lebih banyak kebaikan terkait proses lintas platform:

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

Lihat readme untuk daftar sebagian besar fungsi yang disertakan.


0

Jika menggunakan C ++ 17 seseorang dapat melakukan hal berikut untuk mendapatkan jalur ke file yang dapat dieksekusi.

#include <filesystem>

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

Jawaban di atas telah diuji pada Debian 10 menggunakan G ++ 9.3.0


Perhatikan bahwa ini hanya akan berfungsi jika / proc / self / exe ada dan dapat diakses. Anda mungkin harus memeriksa apakah ini masalahnya.
Zrin

-1

Mulai C ++ 17:

Pastikan Anda menyertakan filesystem std.

#include <filesystem>

dan sekarang Anda bisa melakukan ini.

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

boost filesystem menjadi bagian dari lib standar.

jika Anda tidak dapat menemukannya, coba lihat di bawah:

std::experimental::filesystem

10
Ini bukan jalur biner, ini adalah direktori kerja saat ini.
Zitrax

-2

Ini adalah solusi saya di Windows. Disebut seperti ini:

std::wstring sResult = GetPathOfEXE(64);

Di mana 64 adalah ukuran minimum yang menurut Anda akan dilalui. GetPathOfEXE memanggil dirinya sendiri secara rekursif, menggandakan ukuran buffer setiap kali hingga mendapatkan buffer yang cukup besar untuk mendapatkan seluruh jalur tanpa pemotongan.

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

Apa alasan penggunaan newdan (salah) delete? Jika Anda menggunakan a std::vector, kode Anda tidak akan menunjukkan perilaku yang tidak ditentukan.
IInspectable

Selain itu, GetModuleFileNameWtidak mengatur kode kesalahan terakhir jika berhasil. Kode itu rusak dalam banyak hal. Jangan gunakan jika Anda kebetulan menemukan ini.
IInspectable

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

2
Itu khusus Windows dan menggunakan MFC, sangat jauh dari lintas platform, maaf!
Ben Hymers

1
Ini bahkan bukan cara Windows untuk melakukannya. Lihat PathRemoveFileSpec()dan fungsi terkait sebagai gantinya.
Remy Lebeau

-4

di Unix (termasuk Linux) coba 'yang', di Windows coba 'di mana'.

#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

Metode ini berfungsi untuk Windows dan 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
Ini mengembalikan direktori kerja saat ini, bukan jalur ke eksekusi yang mungkin bukan hal yang sama.
Dave Durbin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.