Pfad der ausführbaren Datei abrufen


114

Ich weiß, dass diese Frage schon einmal gestellt wurde, aber ich habe immer noch keine zufriedenstellende Antwort oder ein definitives "Nein, das geht nicht" gesehen, also werde ich noch einmal fragen!

Ich möchte lediglich den Pfad zur aktuell ausgeführten ausführbaren Datei entweder als absoluten Pfad oder relativ zu dem Ort, von dem aus die ausführbare Datei aufgerufen wird, plattformunabhängig abrufen. Ich dachte, boost :: filesystem :: initial_path war die Antwort auf meine Probleme, aber das scheint nur den 'plattformunabhängigen' Teil der Frage zu behandeln - es gibt immer noch den Pfad zurück, von dem aus die Anwendung aufgerufen wurde.

Für ein bisschen Hintergrundwissen ist dies ein Spiel mit Ogre, das ich mit Very Sleepy zu profilieren versuche, das die ausführbare Zieldatei aus seinem eigenen Verzeichnis ausführt, sodass das Spiel beim Laden natürlich keine Konfigurationsdateien usw. findet und sofort abstürzt . Ich möchte in der Lage sein, ihm einen absoluten Pfad zu den Konfigurationsdateien zu übergeben, von denen ich weiß, dass sie immer neben der ausführbaren Datei leben. Gleiches gilt für das Debuggen in Visual Studio. Ich möchte $ (TargetPath) ausführen können, ohne das Arbeitsverzeichnis festlegen zu müssen.



9
Beachten Sie, dass es unmöglich ist, das Fehlen einer Antwort zu beweisen, daher können Sie kein definitives NEIN erhalten.
Gerne


"Beim Laden findet das Spiel keine Konfigurationsdateien usw. " Also sucht das Spiel nach Konfigurationsdateien im aktuellen Verzeichnis? Das ist eine schlechte Idee und möglicherweise eine Sicherheitslücke. Konfigurationsdateien sollten an einem Standardspeicherort gespeichert werden.
Neugieriger

1
Ich habe hier eine Antwort auf eine verwandte Frage gepostet , die auch Ihre beantwortet und plattformübergreifend mit boost
jtbr

Antworten:


86

Es gibt keinen plattformübergreifenden Weg, den ich kenne.

Für Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
Bei der Plattformunabhängigkeit geht es lediglich darum, die Plattformabhängigkeit zu verbergen. In diesem Fall ist die Verwendung der vordefinierten Betriebssystemmakros unter predef.sourceforge.net/preos.html zur Auswahl der Methode unkompliziert.
Clifford

4
Ist es das, was jeder tut, wenn er den Pfad der ausführbaren Datei in C ++ finden möchte? Ich hatte gehofft, dass etwas so einfach klingendes wie dies bereits in einer Bibliothek wie boost implementiert werden würde.
Ben Hymers

2
@curiousguy Ich bin nicht sicher, ob ich dich verstehe. Ich bin mir ziemlich sicher, dass das der
Ben Hymers

6
@curiousguy: Sie möchten dies tun, wenn Ihr Programm beispielsweise in einem Verzeichnis des Benutzers installiert wird. Sie müssen in der Lage sein, Ihre ausführbare Datei und ihre Unterstützungsdateien irgendwie zu finden.
Greyfade

1
@Duck würdest du deine Antwort mit einem Link zu meiner Bibliothek aktualisieren? Mein Kommentar ist in der Liste vergraben.
Gregory Pakosz

34

Die Funktion boost :: dll :: program_location ist eine der besten plattformübergreifenden Methoden, um den mir bekannten Pfad der laufenden ausführbaren Datei abzurufen. Die DLL-Bibliothek wurde Boost in Version 1.61.0 hinzugefügt.

Das Folgende ist meine Lösung. Ich habe es unter Windows, Mac OS X, Solaris, Free BSD und GNU / Linux getestet.

Es erfordert Boost 1.55.0 oder höher. Es verwendet die Boost.Filesystem-Bibliothek direkt und die Boost.Locale- Bibliothek und die Boost.System- Bibliothek indirekt.

src / executeable_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 / executeable_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 / executeable_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 / executeable_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_

Ich habe ein vollständiges Projekt, einschließlich einer Testanwendung und CMake- Builddateien , die unter SnKOpen - / cpp / executeable_path / trunk verfügbar sind . Diese Version ist vollständiger als die Version, die ich hier bereitgestellt habe. Es werden auch mehr Plattformen unterstützt.

Ich habe die Anwendung auf allen unterstützten Betriebssystemen in den folgenden vier Szenarien getestet.

  1. Relativer Pfad, ausführbar im aktuellen Verzeichnis: dh ./executable_path_test
  2. Relativer Pfad, ausführbar in einem anderen Verzeichnis: dh ./build/executable_path_test
  3. Vollständiger Pfad: dh / some / dir / executeable_path_test
  4. Ausführbar im Pfad, nur Dateiname: dh ausführbarer Pfadpfadtest

In allen vier Szenarien funktionieren sowohl die Funktionen "Ausführbarer_Pfad" als auch "Ausführbarer_Pfad_Fallback" und geben dieselben Ergebnisse zurück.

Anmerkungen

Dies ist eine aktualisierte Antwort auf diese Frage. Ich habe die Antwort aktualisiert, um Benutzerkommentare und -vorschläge zu berücksichtigen. Ich habe auch einen Link zu einem Projekt in meinem SVN-Repository hinzugefügt.


1
Das sieht nach einer sehr vollständigen Lösung mit vernünftigen Rückschlägen aus. +1! Eine Frage: Wäre es sinnvoll, die festen char [1024] -Puffer durch einen Vektor <char> zu ersetzen, dessen Größe geändert werden kann, wenn der Pfad die ursprüngliche Größe überschreitet?
Daniel Wolf

Ja. Das ist ein ausgezeichneter Vorschlag. Natürlich müssten einige zusätzliche Änderungen vorgenommen werden, z. B. das Überprüfen auf Fehler, das Ändern der Puffergröße und das erneute Versuchen.
Ben Key

1
Ich denke, der Fallback ist nicht korrekt. argv[0]kann auch nur ein ausführbarer Name sein. In diesem Fall wäre es erforderlich, PATHauf * nix-Systemen danach zu suchen .
Michał Górny

1
Ich habe es versucht. aber es braucht Auftrieb, richtig? Ich dachte, es wäre Standalone
Manatttta

1
Sie hatten mich bei "boost :: dll :: program_location"
Thomas

31

Auf diese Weise wird Boost + Argv verwendet. Sie haben erwähnt, dass dies möglicherweise nicht plattformübergreifend ist, da es möglicherweise den Namen der ausführbaren Datei enthält oder nicht. Nun, der folgende Code sollte das umgehen.

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

Der folgende Code ruft das aktuelle Arbeitsverzeichnis ab, das möglicherweise das tut, was Sie benötigen

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

Hinweis Ich habe gerade festgestellt, dass basename() veraltet war und daher zu wechseln musste.stem()


stem scheint mir nur die ausführbare Datei abzüglich des Pfads und der Erweiterung unter Windows zu geben, aber das ist ein kleiner Punkt. Was ich gerne wissen würde, ist, wie dies funktioniert, wenn argv [0] möglicherweise falsch ist? Es funktioniert für mich beim Testen unter Windows, aber dann wird argv [0] tatsächlich als absoluter Pfad der ausführbaren Datei übergeben, was die Arbeit von system_complete ziemlich einfach macht :)
Ben Hymers

1
Nein - er braucht das Arbeitsverzeichnis nicht. und NO argv hilft nicht. Was machen Sie, wenn argv nur den ausführbaren Namen enthält? Was tun, wenn das Programm über einen Symlink aufgerufen wurde?
Ichthyo

4
"// Ohne Dateinamen" - Sie wollen .parent_path(), nicht .stem(), nein?
Claudiu

2
Dies scheint auf meiner Plattform (macOS El Capitan) nicht zu funktionieren. Ich bekomme stattdessen das aktuelle Arbeitsverzeichnis. Auch wie @Claudiugesagt, ich denke es sollte sein .parent_path().
Samvv

20

Ich bin mir bei Linux nicht sicher, aber versuche dies für 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
Beachten Sie, dass man für den Fall, dass man mit Unicode-Unterstützung kompiliert, ein WCHAR ownPth..um ein gewickeltes verwenden muss #ifdef UNICODE. Wenn nicht, verwenden Sie den angegebenen Code.
Dr1Ku

1
Nur zur Veranschaulichung: Ich habe nur einen lustigen Fall, in dem GetModuleDirectory einen Pfad mit den Teilen ".." zurückgibt, als würde der String rein roh von der Befehlszeile genommen lol. In diesem Fall startet Visual Studio den Prozess und das .. ist Teil des Debugging-Pfads. so etwas wie $ (projectDir) ../ some.exe Ich habe PathCanonicalize von Shwlib verwendet, aber man muss gegen diese Bibliothek verlinken. Dies ist möglicherweise nicht wünschenswert.
v.oddou

1
Ich würde auch empfehlen, TCHAR für den ownPath anstelle von char zu verwenden. Aber trotzdem eine nette Antwort.
Anhoppe

Kann das überhaupt scheitern? Es scheint auf den ersten Blick unwahrscheinlich ...HMODULE hModule = GetModuleHandle(NULL);
KayleeFrye_onDeck

1
Wenn der erste Parameter für GetModuleFileName NULL ist, wird der Pfad der ausführbaren Datei des aktuellen Prozesses abgerufen.
lsalamon

12

Für Windows:

GetModuleFileName - gibt den exe-Pfad + den exe-Dateinamen zurück

Dateiname entfernen
PathRemoveFileSpec


1
Hinweis für Dokumente zu PathRemoveFileSpec : This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
Javs

12

C ++ 17, Windows, Unicode, mit Dateisystem neue 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;
}

Ich vermute, diese Lösung sollte portabel sein, weiß aber nicht, wie Unicode auf anderen Betriebssystemen implementiert ist.

schwach_canonical wird nur benötigt, wenn Sie als Oberverzeichnis des Ausgabeverzeichnisses Verweise auf den oberen Ordner ('..') verwenden, um den Pfad zu vereinfachen. Wenn Sie es nicht verwenden - entfernen Sie es.

Wenn Sie mit einer dynamischen Linkbibliothek (.dll /.so) arbeiten, haben Sie möglicherweise kein argv. Dann können Sie die folgende Lösung in Betracht ziehen:

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
}

Die Wachen im Header testen nicht ordnungsgemäß auf das Vorhandensein eines Dateisystems. cppreference zeigt, dass der Wert des Feature-Test-Makros im Dateisystem-Header selbst definiert ist. Daher funktioniert das Testen vor dem Einschließen nicht. __has_include () ist hier ein besserer Standardtest.
Meteorhead

8

QT stellt dies mit der Betriebssystemabstraktion als QCoreApplication :: applicationDirPath () bereit.


Erste Schritte damit : QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Irgendeine Idee, wie man das löst?
GuySoft

@GuySoft: Erstellen Sie einfach eine Instanz QCoreApplicationwie diese QApplication application(argc, argv);(tun Sie dies in Ihrem main(argc, argv)und stellen Sie sicher, dass Sie die nicht ändern argc/argv, da diese über die Lebensdauer der QCoreApplication gültig bleiben müssen (siehe Dokumentation )
am

5

Dies ist eine Windows-spezifische Methode, aber mindestens die Hälfte Ihrer Antwort.

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

Ich würde vorschlagen, die Plattformerkennung als Präprozessoranweisung zu verwenden, um die Implementierung einer Wrapper-Funktion zu ändern, die GetThisPathfür jede Plattform aufruft .


3

Verwenden Sie args [0] und suchen Sie nach '/' (oder '\\'):

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

BEARBEITET: Wenn '/' nicht existiert, pos == - 1, damit das Ergebnis korrekt ist.


Was ist, wenn '/' nicht im Pfad vorhanden ist? Es gibt keine Überprüfung dieses Falls und ich glaube, es ist ziemlich wahrscheinlich - Windows verwendet Backslashes und ist args[0]möglicherweise überhaupt kein Pfad.
Ben Hymers

Wenn '/' nicht existiert, gibt rfind -1 zurück, also "path" = aux.substr (0,0) und "name" = aux.substr (0): das Ergebnis ist korrekt. In Bezug auf Windows haben Sie Recht, '/' muss in '\\' geändert werden, ich werde ändern, um auch Windows zuzulassen. Ich habe auch auf Dateinamen mit '/' getestet, aber dieser letzte ist kodifiziert und verursacht keine Probleme.
Adrian Maire

1
Es geht eher darum, args[0]nicht unbedingt der ausführbare Pfad zu sein, der mich stört. Vielen Dank für die Korrektur Ihrer Antwort für Windows :)
Ben Hymers

1
Wenn der Befehl ausgeführt wird, ohne den Pfad anzugeben (dh er befindet sich in einem Verzeichnis, das in der PATH env var angegeben ist), ist args [0] nur der Name der ausführbaren Datei ohne den Pfad.
Kevin

@ Kevin: Sie (und andere) haben Recht, dies ist eine einfache Lösung für kleine Tools, die in ~ 95% der Fälle funktionieren. Bei seriöser Software ist eine Konfigurationsdatei und / oder eine Umgebungsvariable wahrscheinlich besser. Dies impliziert normalerweise auch ein nicht sehr gutes (oder sogar falsches) Design.
Adrian Maire


1

Das Folgende funktioniert als schnelle und schmutzige Lösung, aber beachten Sie, dass es alles andere als narrensicher ist:

#include <iostream>

using namespace std ;

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

17
Ich habe bei anderen SO-Fragen gesehen, dass dies nicht immer funktioniert und dass argv [0] den absoluten Pfad zur ausführbaren Datei, nur den Dateinamen der ausführbaren Datei oder anderen Müll enthalten kann.
Ben Hymers

7
Man sollte argv [0] niemals vertrauen, wenn man versucht, "Support-Dateien" oder ähnliches zu öffnen. Argv kann sich ändern, und jeder Anrufer, der böse ist, kann den Wert ändern. Vermeiden Sie es, es sei denn, Sie verwenden es zum Protokollieren usw., NICHT zum Erstellen von Pfaden zum Öffnen von Dateien.
Qix - MONICA wurde

Dies funktioniert nicht unter Windows. argv [0] hat nicht den vollständigen Pfad. Nur die EXE-Datei. Bitte versuchen Sie es nicht in einer Bash-Shell, sondern in der Standardkonsole und cout << argv [0], um sie zu reproduzieren.
Freddy Martinez Garcia

@FreddyMartinezGarcia Nun, ich hätte es in Windows getestet, also YMMV. Es ist alles, was zum Starten des Codes verwendet wurde. Wenn Sie die ausführbare Datei im CWD eingeben, erhalten Sie sicher nur den Dateinamen.
Clifford

0

Falls Sie Unicode-Pfade für Windows verarbeiten müssen:

#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

Unter Windows besteht das Problem, wie die ausführbare Datei aus dem Ergebnis von entfernt werden kann GetModuleFileName(). Der Windows-API-Aufruf PathRemoveFileSpec(), den Nate in seiner Antwort zu diesem Zweck verwendet hat, hat sich zwischen Windows 8 und seinen Vorgängern geändert. Wie kann man also mit beiden kompatibel und sicher bleiben? Zum Glück gibt es C ++ 17 (oder Boost, wenn Sie einen älteren Compiler verwenden). Ich mache das:

#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

Wie andere bereits erwähnt haben, argv[0]ist dies eine gute Lösung, vorausgesetzt, die Plattform übergibt tatsächlich den ausführbaren Pfad, was sicherlich nicht weniger wahrscheinlich ist als das Betriebssystem Windows (wobei WinAPI dabei helfen kann, den ausführbaren Pfad zu finden). Wenn Sie die Zeichenfolge entfernen möchten, um nur den Pfad zu dem Verzeichnis einzuschließen, in dem sich die ausführbare Datei befindet, ist die Verwendung dieses Pfads zum Suchen anderer Anwendungsdateien (z. B. Spiel-Assets, wenn Ihr Programm ein Spiel ist) vollkommen in Ordnung, da das Öffnen von Dateien relativ zu ist das Arbeitsverzeichnis oder, falls angegeben, das Stammverzeichnis.


0

Damit bin ich gelandet

Die Header-Datei sieht folgendermaßen aus:

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

}

Implementierung


#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

Die SDL2- Bibliothek ( https://www.libsdl.org/ ) verfügt über zwei Funktionen, die auf einem breiten Spektrum von Plattformen implementiert sind:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Wenn Sie also das Rad nicht neu erfinden möchten ... bedeutet dies leider, dass Sie die gesamte Bibliothek einbeziehen, obwohl es eine recht zulässige Lizenz hat und man den Code auch einfach kopieren kann. Außerdem bietet es viele andere plattformübergreifende Funktionen.


0

Dies ist wahrscheinlich der natürlichste Weg, dies zu tun, während die meisten wichtigen Desktop-Plattformen abgedeckt werden. Ich bin nicht sicher, aber ich glaube, dass dies mit allen BSDs funktionieren sollte, nicht nur mit FreeBSD, wenn Sie die Plattformmakroprüfung so ändern, dass sie alle abdeckt. Wenn ich jemals Solaris installieren sollte, werde ich diese Plattform sicher zur Liste der unterstützten hinzufügen.

Bietet volle UTF-8-Unterstützung unter Windows, die nicht jedem wichtig genug ist, um so weit zu gehen.

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

Dies ermöglicht es, den vollständigen Pfad zur ausführbaren Datei von so ziemlich jeder Prozess-ID abzurufen, außer unter Windows gibt es einige Prozesse mit Sicherheitsattributen, die dies einfach nicht zulassen. Daher ist diese Lösung nicht perfekt.

Um die Frage genauer zu beantworten, können Sie Folgendes tun:

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

Erstellen Sie die obige Dateistruktur mit diesem Befehl:

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

Zum Herunterladen einer Kopie der oben aufgeführten Dateien:

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

Für mehr plattformübergreifende prozessbezogene Güte:

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

In der Readme-Datei finden Sie eine Liste der meisten enthaltenen Funktionen.


-1

Ab C ++ 17:

Stellen Sie sicher, dass Sie das Standard-Dateisystem einschließen.

#include <filesystem>

und jetzt kannst du das machen.

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

Das Boost-Dateisystem wurde Teil der Standardbibliothek.

Wenn Sie es nicht finden können, schauen Sie unter:

std::experimental::filesystem

10
Dies ist nicht der Pfad der Binärdatei, sondern das aktuelle Arbeitsverzeichnis.
Zitrax

-2

Dies war meine Lösung in Windows. Es heißt so:

std::wstring sResult = GetPathOfEXE(64);

Wobei 64 die Mindestgröße ist, von der Sie glauben, dass der Pfad sein wird. GetPathOfEXE ruft sich selbst rekursiv auf und verdoppelt jedes Mal die Größe des Puffers, bis er einen Puffer erhält, der groß genug ist, um den gesamten Pfad ohne Kürzung abzurufen.

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

Was ist der Grund für die Verwendung newund die (falsche) delete? Wenn Sie a verwendet hätten std::vector, hätte Ihr Code kein undefiniertes Verhalten gezeigt.
Unsichtbarer

Außerdem wird GetModuleFileNameWbei Erfolg nicht der letzte Fehlercode festgelegt. Dieser Code ist in vielerlei Hinsicht kaputt. Nicht verwenden, wenn Sie zufällig darüber stolpern.
Unsichtbarer

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

2
Das ist nur Windows und verwendet MFC, also weit davon entfernt, plattformübergreifend zu sein, sorry!
Ben Hymers

1
Dies ist auch nicht die Windows-Methode. Schauen Sie sich PathRemoveFileSpec()stattdessen die zugehörigen Funktionen an.
Remy Lebeau

-4

Versuchen Sie unter Unix (einschließlich Linux) 'which', unter 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

Diese Methode funktioniert sowohl unter Windows als auch unter 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
Dies gibt das aktuelle Arbeitsverzeichnis zurück, nicht den Pfad zur ausführbaren Datei, der möglicherweise nicht dasselbe ist.
Dave Durbin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.