Obtenir le chemin de l'exécutable

115

Je sais que cette question a déjà été posée mais je n'ai toujours pas vu de réponse satisfaisante, ou un "non, cela ne peut pas être fait", alors je vais demander à nouveau!

Tout ce que je veux faire, c'est obtenir le chemin d'accès à l'exécutable en cours d'exécution, soit en tant que chemin absolu, soit par rapport à l'endroit d'où l'exécutable est appelé, d'une manière indépendante de la plate-forme. Je pensais que boost :: filesystem :: initial_path était la réponse à mes problèmes, mais cela ne semble gérer que la partie «indépendante de la plate-forme» de la question - il renvoie toujours le chemin à partir duquel l'application a été appelée.

Pour un peu de contexte, il s'agit d'un jeu utilisant Ogre, que j'essaie de profiler en utilisant Very Sleepy, qui exécute l'exécutable cible à partir de son propre répertoire, donc bien sûr, lors du chargement, le jeu ne trouve aucun fichier de configuration, etc. et se bloque rapidement. . Je veux pouvoir lui transmettre un chemin absolu vers les fichiers de configuration, qui, je le sais, vivront toujours avec l'exécutable. Il en va de même pour le débogage dans Visual Studio - j'aimerais pouvoir exécuter $ (TargetPath) sans avoir à définir le répertoire de travail.

Ben Hymers
la source
3
stackoverflow.com/questions/1023306/… et autres
dmckee --- ancien chaton modérateur
9
Notez qu'il est impossible de prouver l'absence de réponse, donc vous ne pouvez pas obtenir un NON définitif . Je serai heureux de vous donner un NON faisant autorité :)
MSalters
duplication possible de la façon de trouver l'emplacement de l'exécutable en C
ergosys
" au chargement, le jeu ne trouve aucun fichier de configuration, etc. " donc le jeu recherche les fichiers de configuration dans le répertoire courant? C'est une mauvaise idée et potentiellement une vulnérabilité de sécurité. Les fichiers de configuration doivent être stockés dans un emplacement standard.
curiousguy
1
J'ai posté une réponse ici à une question connexe qui répond également à la vôtre, travaillant sur plusieurs plates-formes à l'aide de boost
jtbr

Réponses:

86

Il n'y a aucun moyen multiplateforme que je connaisse.

Pour Linux: readlink / proc / self / exe

Windows: GetModuleFileName

canard
la source
9
L'indépendance de la plateforme consiste simplement à masquer la dépendance de la plateforme. Dans ce cas, l'utilisation des macros OS prédéfinies détaillées sur predef.sourceforge.net/preos.html pour sélectionner la méthode est simple.
Clifford
4
Alors, est-ce ce que tout le monde fait quand il veut trouver le chemin de l'exécutable en C ++? J'espérais que quelque chose d'aussi simple que cela serait déjà implémenté dans une bibliothèque comme boost.
Ben Hymers
2
@curiousguy Je ne suis pas sûr de vous comprendre; Je suis à peu près sûr que c'est le but de cette question :)
Ben Hymers
6
@curiousguy: Vous voudriez le faire si, par exemple, votre programme peut être installé dans un répertoire choisi par l'utilisateur. Vous devez être en mesure de trouver votre exécutable et ses fichiers de support d'une manière ou d'une autre.
greyfade
1
@Duck voudriez-vous mettre à jour votre réponse avec un lien vers ma bibliothèque? Mon commentaire est enterré dans la liste.
Gregory Pakosz
35

Le boost :: dll :: program_location est l'une des meilleures méthodes multiplateformes pour obtenir le chemin de l'exécutable en cours d'exécution que je connaisse. La bibliothèque DLL a été ajoutée à Boost dans la version 1.61.0.

Voici ma solution. Je l'ai testé sur Windows, Mac OS X, Solaris, Free BSD et GNU / Linux.

Il nécessite Boost 1.55.0 ou supérieur. Il utilise la bibliothèque Boost.Filesystem directement et la Boost.Locale bibliothèque et Boost.System bibliothèque indirectement.

src / chemin_exécutable.cpp

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

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

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

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

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

namespace boost {

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)

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

#elif (BOOST_OS_MACOS)

#  include <mach-o/dyld.h>

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

#elif (BOOST_OS_SOLARIS)

#  include <stdlib.h>

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

#elif (BOOST_OS_BSD)

#  include <sys/sysctl.h>

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

#elif (BOOST_OS_LINUX)

#  include <unistd.h>

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

#else

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

#endif

}

src / detail / executable_path_internals.cpp

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

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

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

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

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

namespace boost {
namespace detail {

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

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

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

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

}
}

include / boost / executable_path.hpp

#ifndef BOOST_EXECUTABLE_PATH_HPP_
#define BOOST_EXECUTABLE_PATH_HPP_

#pragma once

#include <string>

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

#endif // BOOST_EXECUTABLE_PATH_HPP_

include / boost / detail / executable_path_internals.hpp

#ifndef BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_
#define BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

#pragma once

#include <string>
#include <vector>

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

#endif // BOOST_DETAIL_EXECUTABLE_PATH_INTERNALS_HPP_

J'ai un projet complet, y compris une application de test et des fichiers de construction CMake disponibles sur SnKOpen - / cpp / executable_path / trunk . Cette version est plus complète que la version que j'ai fournie ici. Il prend également en charge plus de plates-formes.

J'ai testé l'application sur tous les systèmes d'exploitation pris en charge dans les quatre scénarios suivants.

  1. Chemin relatif, exécutable dans le répertoire courant: ie ./executable_path_test
  2. Chemin relatif, exécutable dans un autre répertoire: ie ./build/executable_path_test
  3. Chemin complet: ie / some / dir / executable_path_test
  4. Exécutable dans le chemin, nom de fichier uniquement: c.-à-d. Chemin_exécutable_test

Dans les quatre scénarios, les fonctions executable_path et executable_path_fallback fonctionnent et renvoient les mêmes résultats.

Remarques

Ceci est une réponse mise à jour à cette question. J'ai mis à jour la réponse pour prendre en compte les commentaires et suggestions des utilisateurs. J'ai également ajouté un lien vers un projet dans mon référentiel SVN.

Ben Key
la source
1
Cela ressemble à une solution très complète avec des solutions de rechange raisonnables. +1! Une question, cependant: serait-il judicieux de remplacer les tampons fixes char [1024] par quelque chose comme un vecteur <char> qui peut être redimensionné si le chemin dépasse la taille initiale?
Daniel Wolf
Oui. C'est une excellente suggestion. Bien sûr, des modifications supplémentaires devraient être apportées, telles que la vérification des erreurs, le redimensionnement du tampon et une nouvelle tentative.
Ben Key
1
Je pense que le repli n'est pas correct. argv[0]peut également être simplement un nom d'exécutable, auquel cas il serait nécessaire de le rechercher dans PATHles systèmes * nix.
Michał Górny
1
J'ai essayé de l'utiliser. mais il a besoin de boost, correct? Je pensais que c'était autonome
manatttta
1
Vous m'avez eu à "boost :: dll :: program_location"
Thomas
31

Cette façon utilise boost + argv. Vous avez mentionné que cela peut ne pas être multiplateforme car il peut inclure ou non le nom de l'exécutable. Eh bien, le code suivant devrait contourner ce problème.

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

Le code suivant obtient le répertoire de travail actuel qui peut faire ce dont vous avez besoin

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

Remarque Je viens de me rendre compte que basename() était obsolète et a donc dû passer à.stem()

Ryu
la source
stem semble me donner juste l'exécutable moins le chemin et l'extension sous Windows, mais c'est un point mineur. Ce que j'aimerais savoir, c'est comment cela fonctionne quand argv [0] est peut-être incorrect? Cela fonctionne pour moi en testant sur Windows, mais ensuite argv [0] est en fait passé comme chemin absolu de l'exécutable, ce qui rend le travail de system_complete assez facile :)
Ben Hymers
1
Non - il n'a pas besoin du répertoire de travail. et NO argv n'aide pas. Que faites-vous lorsque argv ne contient que le nom de l'exécutable? Que faire, lorsque le programme a été appelé via un lien symbolique?
Ichthyo
4
"// Sans nom de fichier" - vous voulez .parent_path(), non .stem(), non?
Claudiu
2
Cela ne semble pas fonctionner sur ma plate-forme (macOS El Capitan). J'obtiens le répertoire de travail actuel à la place. De plus, comme @Claudiuje l'ai dit, je pense que cela devrait l'être .parent_path().
samvv
20

Je ne suis pas sûr de Linux, mais essayez ceci pour 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;
     }
}
SturmCoder
la source
3
Notez qu'il faut utiliser WCHAR ownPth.., encapsulé autour d'un #ifdef UNICODEdans le cas où l'on compile avec le support Unicode. Sinon, utilisez le code fourni.
Dr1Ku
1
juste pour la petite histoire, j'ai juste un cas drôle où GetModuleDirectory renvoie un chemin avec les parties ".." dedans, comme s'il prenait la chaîne pure raw de la ligne de commande lol. en fait, dans ce cas, visual studio lance le processus et le .. fait partie du chemin de débogage. quelque chose comme $ (projectDir) ../ some.exe J'ai utilisé PathCanonicalize de Shwlib mais il faut établir un lien avec cette lib. cela peut ne pas être souhaitable.
v.oddou
1
Je recommande également d'utiliser TCHAR pour ownPath au lieu de char. Mais bonne réponse quand même.
anhoppe
Est-il même possible que cela échoue? Cela semble peu probable en un coup d'œil ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck
1
Si le premier paramètre de GetModuleFileName est NULL, il récupère le chemin du fichier exécutable du processus en cours.
lsalamon le
12

Pour les fenêtres:

GetModuleFileName - renvoie le chemin exe + le nom du fichier exe

Pour supprimer le nom de fichier
PathRemoveFileSpec

Sivabalan
la source
1
Docs pour noter PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs
12

C ++ 17, Windows, Unicode, en utilisant la nouvelle API du système de fichiers:

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

Je pense que cette solution devrait être portable, mais je ne sais pas comment unicode est implémenté sur d'autres systèmes d'exploitation.

lowly_canonical n'est nécessaire que si vous utilisez comme références de dossier supérieur du répertoire de sortie ('..') pour simplifier le chemin. Si vous ne l'utilisez pas, supprimez-le.

Si vous utilisez une bibliothèque de liens dynamiques (.dll /.so), vous n'avez peut-être pas argv, vous pouvez envisager la solution suivante:

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
}
TarmoPikaro
la source
Les gardes à l'intérieur de l'en-tête ne testent pas correctement la présence du système de fichiers. cppreference montre que la valeur de la macro de test de fonctionnalité est définie dans l'en-tête du système de fichiers lui-même, donc il ne fonctionne pas pour tester avant l'inclusion. __has_include () est un meilleur test standard ici.
Meteorhead
8

QT fournit cela avec l'abstraction du système d'exploitation comme QCoreApplication :: applicationDirPath ()

ted
la source
Obtenir avec ceci: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Une idée de comment résoudre ça?
GuySoft
@GuySoft: créez simplement une instance de ce QCoreApplicationtype QApplication application(argc, argv);(faites-le dans votre main(argc, argv), et assurez-vous de ne pas modifier le argc/argv, car ceux-ci doivent rester valides pendant toute la durée de vie de QCoreApplication (consultez la documentation )
ted
5

C'est une méthode spécifique à Windows, mais c'est au moins la moitié de votre réponse.

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

Je suggérerais d'utiliser la détection de plate-forme comme directives de préprocesseur pour modifier l'implémentation d'une fonction wrapper qui appelle GetThisPathchaque plate-forme.

Nate
la source
3

En utilisant args [0] et en recherchant '/' (ou '\\'):

#include <string>
#include <iostream> // to show the result

int main( int numArgs, char *args[])
{
    // Get the last position of '/'
    std::string aux(args[0]);

    // get '/' or '\\' depending on unix/mac or windows.
#if defined(_WIN32) || defined(WIN32)
    int pos = aux.rfind('\\');
#else
    int pos = aux.rfind('/');
#endif

    // Get the path and the name
    std::string path = aux.substr(0,pos+1);
    std::string name = aux.substr(pos+1);
    // show results
    std::cout << "Path: " << path << std::endl;
    std::cout << "Name: " << name << std::endl;
}

EDITED: Si '/' n'existe pas, pos == - 1 donc le résultat est correct.

Adrian Maire
la source
Et si «/» n'est pas présent dans le chemin? Il n'y a pas de vérification de ce cas et je pense que c'est très probable - Windows utilisera des barres obliques inverses et args[0]ne sera peut - être pas du tout un chemin.
Ben Hymers
Si '/' n'existe pas, rfind renvoie -1, donc "path" = aux.substr (0,0) et "name" = aux.substr (0): le résultat est correct. En ce qui concerne Windows, vous avez raison, «/» doit être remplacé par «\\», je changerai pour autoriser les fenêtres aussi. J'ai également testé des noms de fichiers avec '/', mais ce dernier est codifié et ne crée pas de problèmes.
Adrian Maire du
1
C'est plus le fait de args[0]ne pas nécessairement être le chemin de l'exécutable qui me dérange. Merci d'avoir corrigé votre réponse pour Windows :)
Ben Hymers
1
Si la commande est exécutée sans donner le chemin (c'est-à-dire qu'elle est trouvée en étant dans un répertoire donné dans la variable PATH env), args [0] sera juste le nom de l'exécutable, sans le chemin.
Kevin le
@Kevin: vous (et les autres) avez raison, c'est une solution simple, pour les petits outils, qui fonctionnent ~ 95% des cas. Pour les logiciels sérieux, un fichier de configuration et / ou une variable d'environnement est probablement préférable. En outre, ce besoin implique généralement une conception pas très bonne (ou même fausse).
Adrian Maire du
1

Ce qui suit fonctionne comme une solution rapide et sale, mais notez que c'est loin d'être infaillible:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}
Clifford
la source
17
J'ai vu sur d'autres questions SO que cela ne fonctionne pas toujours, et que argv [0] peut contenir le chemin absolu de l'exécutable, juste le nom de fichier de l'exécutable, ou toute autre poubelle.
Ben Hymers
7
Il ne faut jamais faire confiance à argv [0] s'ils essaient d'ouvrir des 'fichiers de support' ou autres. Argv est sujet à changement, et tout appelant maléfique peut en changer la valeur. Évitez à moins que vous ne l'utilisiez pour la journalisation, etc., PAS pour construire des chemins utilisés pour ouvrir des fichiers.
Qix - MONICA a été égaré
cela ne fonctionne pas sous Windows. argv [0] n'aura pas le chemin complet. Seul le fichier .exe. S'il vous plaît, n'essayez pas dans un shell bash, essayez-le dans la console standard et cout << argv [0] pour le reproduire.
Freddy Martinez Garcia
@FreddyMartinezGarcia Eh bien, je l'aurais testé sous Windows, donc YMMV. C'est tout ce qui a été utilisé pour lancer le code. Si vous utilisez l'exécutable dans le CWD, vous n'obtiendrez que le nom du fichier.
Clifford
0

Au cas où vous auriez besoin de gérer les chemins Unicode pour 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;
}
kayleeFrye_onDeck
la source
0

Pour Windows, vous avez le problème de savoir comment supprimer l'exécutable du résultat de GetModuleFileName(). L'appel de l'API Windows PathRemoveFileSpec()que Nate a utilisé à cette fin dans sa réponse a changé entre Windows 8 et ses prédécesseurs. Alors, comment rester compatible avec les deux et en toute sécurité? Heureusement, il existe C ++ 17 (ou Boost, si vous utilisez un compilateur plus ancien). Je fais ça:

#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.
}
tobi_s
la source
0

Comme d'autres l'ont mentionné, argv[0]c'est une solution plutôt intéressante, à condition que la plate-forme passe réellement le chemin de l'exécutable, ce qui n'est sûrement pas moins probable que le système d'exploitation Windows (où WinAPI peut aider à trouver le chemin de l'exécutable). Si vous souhaitez supprimer la chaîne pour n'inclure que le chemin du répertoire où réside l'exécutable, alors utiliser ce chemin pour trouver d'autres fichiers d'application (comme les ressources de jeu si votre programme est un jeu) est parfaitement bien, car l'ouverture des fichiers est relative à le répertoire de travail ou, le cas échéant, la racine.

Kotauskas
la source
0

C'est ce avec quoi j'ai fini

Le fichier d'en-tête ressemble à ceci:

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

}

la mise en oeuvre


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

}
Atul
la source
0

La bibliothèque SDL2 ( https://www.libsdl.org/ ) a deux fonctions implémentées sur un large éventail de plates-formes:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Donc, si vous ne voulez pas réinventer la roue ... malheureusement, cela signifie inclure toute la bibliothèque, bien qu'elle ait une licence assez permissive et que l'on puisse aussi simplement copier le code. En outre, il fournit de nombreuses autres fonctionnalités multiplateformes.

user1050755
la source
0

C'est probablement la manière la plus naturelle de le faire, tout en couvrant la plupart des principales plates-formes de bureau. Je ne suis pas sûr, mais je pense que cela devrait fonctionner avec tous les BSD, pas seulement FreeBSD, si vous modifiez la vérification des macros de la plate-forme pour les couvrir tous. Si jamais j'arrive à installer Solaris, je veillerai à ajouter cette plate-forme à la liste prise en charge.

Prise en charge complète de UTF-8 sur Windows, ce que tout le monde ne se soucie pas suffisamment pour aller aussi loin.

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

Cela permet d'obtenir le chemin complet vers l'exécutable de quasiment n'importe quel identifiant de processus, sauf sous Windows, il existe des processus avec des attributs de sécurité qui ne le permettront tout simplement pas, donc wysiwyg, cette solution n'est pas parfaite.

Pour répondre plus précisément à ce que la question posait, vous pouvez faire ceci:

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

Construisez la structure de fichier ci-dessus avec cette commande:

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

Pour télécharger une copie des fichiers répertoriés ci-dessus:

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

Pour plus de qualité liée aux processus multiplateformes:

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

Voir le readme pour une liste de la plupart des fonctions incluses.

Samuel Joseph Venable
la source
0

Si vous utilisez C ++ 17, vous pouvez faire ce qui suit pour obtenir le chemin d'accès à l'exécutable.

#include <filesystem>

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

La réponse ci-dessus a été testée sur Debian 10 en utilisant G ++ 9.3.0

Scillman
la source
Notez que cela ne fonctionnera que si / proc / self / exe existe et est accessible. Vous devriez probablement vérifier si tel est le cas.
Zrin
-1

À partir de C ++ 17:

Assurez-vous d'inclure le système de fichiers std.

#include <filesystem>

et maintenant vous pouvez le faire.

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

Le système de fichiers boost est devenu une partie de la bibliothèque standard.

si vous ne le trouvez pas, essayez de regarder ci-dessous:

std::experimental::filesystem
Tomer Zeitune
la source
10
Ce n'est pas le chemin du binaire, c'est le répertoire de travail actuel.
Zitrax
-2

C'était ma solution sous Windows. Il s'appelle ainsi:

std::wstring sResult = GetPathOfEXE(64);

Où 64 est la taille minimale que vous pensez que le chemin sera. GetPathOfEXE s'appelle lui-même récursivement, doublant la taille du tampon à chaque fois jusqu'à ce qu'il obtienne un tampon suffisamment grand pour obtenir le chemin complet sans troncature.

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());
    }
}
Timothy John Laird
la source
Quelle est la raison de l'utilisation newet le (mauvais) delete? Si vous aviez utilisé a std::vector, votre code n'aurait pas présenté de comportement indéfini.
IInspectable
De plus, GetModuleFileNameWne définit pas le dernier code d'erreur en cas de succès. Ce code est cassé à bien des égards. Ne l'utilisez pas si vous tombez dessus.
IInspectable
-3
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
Vishwa
la source
2
C'est uniquement Windows et utilise MFC, donc loin d'être multiplateforme, désolé!
Ben Hymers
1
Ce n'est même pas la manière Windows de le faire non plus. Jetez PathRemoveFileSpec()plutôt un œil aux fonctions associées.
Remy Lebeau
-4

sous Unix (y compris Linux), essayez «qui», sous Windows, essayez «où».

#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;
}
chmn
la source
-4

Cette méthode fonctionne à la fois pour Windows et 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;
}
James Mart
la source
2
Cela renvoie le répertoire de travail actuel, pas le chemin vers l'exécutable qui peut ne pas être la même chose.
Dave Durbin