Ottieni il percorso dell'eseguibile


114

So che questa domanda è stata posta in passato ma non ho ancora visto una risposta soddisfacente, o un definitivo "no, non si può fare", quindi lo chiederò di nuovo!

Tutto quello che voglio fare è ottenere il percorso dell'eseguibile attualmente in esecuzione, come percorso assoluto o relativo a dove viene richiamato l'eseguibile, in modo indipendente dalla piattaforma. Pensavo che boost :: filesystem :: initial_path fosse la risposta ai miei problemi, ma sembra che gestisca solo la parte "indipendente dalla piattaforma" della domanda: restituisce comunque il percorso da cui è stata invocata l'applicazione.

Per un po 'di background, questo è un gioco che utilizza Ogre, che sto cercando di profilare usando Very Sleepy, che esegue l'eseguibile di destinazione dalla propria directory, quindi ovviamente al caricamento il gioco non trova file di configurazione ecc. E si blocca prontamente . Voglio essere in grado di passargli un percorso assoluto ai file di configurazione, che so che vivranno sempre accanto all'eseguibile. Lo stesso vale per il debug in Visual Studio: mi piacerebbe poter eseguire $ (TargetPath) senza dover impostare la directory di lavoro.



9
Nota che è impossibile dimostrare l'assenza di una risposta, quindi non puoi ottenere un NO definitivo . Sarò felice di darti un NO autorevole :)
MSalters


" al caricamento il gioco non trova file di configurazione ecc. " quindi il gioco cerca i file di configurazione nella directory corrente? Questa è una cattiva idea e potenzialmente una vulnerabilità di sicurezza. I file di configurazione devono essere archiviati in una posizione standard.
curioso

1
Ho pubblicato una risposta qui a una domanda correlata che risponde anche alla tua, lavorando su piattaforme utilizzando boost
jtbr

Risposte:


86

Non esiste un modo multipiattaforma che io conosca.

Per Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
L'indipendenza dalla piattaforma è semplicemente una questione di nascondere la dipendenza dalla piattaforma. In questo caso, l'utilizzo delle macro del sistema operativo predefinite dettagliate in predef.sourceforge.net/preos.html per selezionare il metodo è semplice.
Clifford

4
Quindi è questo quello che fanno tutti ogni volta che vogliono trovare il percorso dell'eseguibile in C ++? Speravo che qualcosa di così semplice come questo sarebbe già stato implementato in una libreria come boost.
Ben Hymers

2
@curiousguy Non sono sicuro di capirti; Sono abbastanza sicuro che questo sia il punto centrale di questa domanda :)
Ben Hymers

6
@curiousguy: vorresti farlo se, ad esempio, il tuo programma potesse essere installato in una directory scelta dall'utente. Devi essere in grado di trovare il tuo eseguibile e i suoi file di supporto in qualche modo.
dissolvenza grigia

1
@ Duck aggiorneresti la tua risposta con un link alla mia libreria? Il mio commento è sepolto in fondo alla lista.
Gregory Pakosz

35

Il boost :: dll :: program_location è uno dei migliori metodi multipiattaforma per ottenere il percorso dell'eseguibile in esecuzione che io conosca. La libreria DLL è stata aggiunta a Boost nella versione 1.61.0.

La seguente è la mia soluzione. L'ho testato su Windows, Mac OS X, Solaris, Free BSD e GNU / Linux.

Richiede Boost 1.55.0 o superiore. Esso utilizza la libreria Boost.Filesystem direttamente e il Boost.Locale biblioteca e Boost.System biblioteca indirettamente.

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

Ho un progetto completo, inclusa un'applicazione di test e file di build CMake disponibili su SnKOpen - / cpp / executable_path / trunk . Questa versione è più completa della versione che ho fornito qui. Supporta anche più piattaforme.

Ho testato l'applicazione su tutti i sistemi operativi supportati nei seguenti quattro scenari.

  1. Percorso relativo, eseguibile nella directory corrente: es ./executable_path_test
  2. Percorso relativo, eseguibile in un'altra directory: es ./build/executable_path_test
  3. Percorso completo: ie / some / dir / executable_path_test
  4. Eseguibile in path, solo nome file: ie executable_path_test

In tutti e quattro gli scenari, entrambe le funzioni executable_path e executable_path_fallback funzionano e restituiscono gli stessi risultati.

Appunti

Questa è una risposta aggiornata a questa domanda. Ho aggiornato la risposta per prendere in considerazione i commenti e i suggerimenti degli utenti. Ho anche aggiunto un collegamento a un progetto nel mio repository SVN.


1
Sembra una soluzione molto completa con ragionevoli fallback. +1! Una domanda, però: avrebbe senso sostituire i buffer char [1024] fissi con qualcosa come un vettore <char> che può essere ridimensionato se il percorso supera la dimensione iniziale?
Daniel Wolf

Sì. Questo è un ottimo suggerimento. Ovviamente sarebbe necessario apportare alcune modifiche aggiuntive come il controllo degli errori, il ridimensionamento del buffer e il tentativo di nuovo.
Ben Key

1
Penso che il fallback non sia corretto. argv[0]può anche essere solo il nome dell'eseguibile, nel qual caso sarebbe necessario cercarlo nei PATHsistemi * nix.
Michał Górny

1
Ho provato a usarlo. ma ha bisogno di spinta, giusto? Ho pensato che fosse autonomo
manatttta il

1
Mi avevi a "boost :: dll :: program_location"
Thomas

31

In questo modo usa boost + argv. Hai detto che potrebbe non essere multipiattaforma perché potrebbe includere o meno il nome dell'eseguibile. Bene, il codice seguente dovrebbe aggirare questo problema.

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

Il codice seguente ottiene la directory di lavoro corrente che potrebbe fare ciò di cui hai bisogno

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

Nota Ho appena realizzato che basename() era deprecato, quindi ho dovuto passare a.stem()


stem sembra fornirmi solo l'eseguibile meno il percorso e l'estensione su Windows, ma questo è un punto minore. Quello che vorrei sapere è come funziona se argv [0] potrebbe essere errato? Funziona per me test su Windows, ma poi argv [0] viene effettivamente passato come percorso assoluto dell'eseguibile, il che rende il lavoro di system_complete abbastanza facile :)
Ben Hymers

1
No, non ha bisogno della directory di lavoro. e NO argv non aiuta. Cosa fai quando argv contiene solo il nome dell'eseguibile? Cosa fare, quando il programma è stato richiamato tramite un collegamento simbolico?
Ichthyo

4
"// Senza nome file" - vuoi .parent_path(), no .stem(), no?
Claudiu

2
Questo non sembra funzionare sulla mia piattaforma (macOS El Capitan). Ottengo invece la directory di lavoro corrente. Inoltre, come @Claudiudetto, penso che dovrebbe essere .parent_path().
samvv

20

Non sono sicuro di Linux, ma prova questo per 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
Si noti che è necessario utilizzare WCHAR ownPth.., avvolto attorno a un #ifdef UNICODEnel caso in cui si compili con supporto Unicode. In caso contrario, utilizzare il codice fornito.
Dr1Ku

1
solo per la cronaca, ho solo un caso divertente in cui GetModuleDirectory restituisce un percorso con le parti "..", come se prendesse la stringa pura e grezza dalla riga di comando lol. in realtà in questo caso Visual Studio sta avviando il processo e .. fa parte del percorso di debug. qualcosa come $ (projectDir) ../ some.exe Ho usato PathCanonicalize da Shwlib ma è necessario collegarsi a questa libreria. questo potrebbe non essere desiderabile.
v.oddou

1
Consiglio anche di usare TCHAR per ownPath invece di char. Ma bella risposta comunque.
anhoppe

È anche possibile che questo fallisca? Sembra improbabile a colpo d'occhio ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
Se il primo parametro per GetModuleFileName è NULL, recupera il percorso del file eseguibile del processo corrente.
lsalamon,

12

Per Windows:

GetModuleFileName - restituisce il percorso exe + il nome file exe

Per rimuovere il nome del file
PathRemoveFileSpec


1
Documenti nota per PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs

12

C ++ 17, Windows, Unicode, utilizzando il nuovo file system 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;
}

Sospetto che questa soluzione dovrebbe essere portabile, ma non so come sia implementato Unicode su altri sistemi operativi.

weakly_canonical è necessario solo se si utilizzano come riferimenti alla cartella superiore della directory di output ("..") per semplificare il percorso. Se non lo usi, rimuovilo.

Se stai operando dalla libreria di collegamento dinamico (.dll /.so), potresti non avere argv, quindi puoi prendere in considerazione la seguente soluzione:

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
}

Le protezioni all'interno dell'intestazione non stanno verificando correttamente la presenza del filesystem. cppreference mostra che il valore della macro di test delle funzionalità è definito nell'intestazione del filesystem stesso, quindi non funziona per testare prima di includere. __has_include () è un test standard migliore qui.
Meteorhead

8

QT fornisce questo con l'astrazione del sistema operativo come QCoreApplication :: applicationDirPath ()


Ottenere questo: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Qualche idea su come risolverlo?
GuySoft

@GuySoft: crea semplicemente un'istanza del QCoreApplicationgenere QApplication application(argc, argv);(fallo nel tuo main(argc, argv)e assicurati di non modificare il argc/argv, poiché questi devono rimanere validi per tutta la durata di QCoreApplication (controlla la documentazione )
ted

5

Questo è un modo specifico per Windows, ma è almeno la metà della tua risposta.

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

Suggerirei di utilizzare il rilevamento della piattaforma come direttive del preprocessore per modificare l'implementazione di una funzione wrapper che chiama GetThisPathper ciascuna piattaforma.


3

Utilizzando args [0] e cercando "/" (o "\\"):

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

MODIFICATO: Se "/" non esiste, pos == - 1 quindi il risultato è corretto.


E se "/" non fosse presente nel percorso? Non c'è controllo di quel caso e credo che sia abbastanza probabile: Windows utilizzerà i backslash e args[0]potrebbe non essere affatto un percorso.
Ben Hymers

Se '/' non esiste, rfind restituisce -1, quindi "path" = aux.substr (0,0) e "name" = aux.substr (0): il risultato è corretto. Relativamente a Windows, hai ragione, "/" deve essere cambiato in "\\", cambierò per consentire anche a Windows. Ho anche testato nomi di file con '/', ma quest'ultimo è codificato e non crea problemi.
Adrian Maire

1
È più la parte che args[0]non è necessariamente il percorso eseguibile che mi dà fastidio. Grazie per aver corretto la tua risposta per Windows però :)
Ben Hymers

1
Se il comando viene eseguito senza fornire il percorso (ovvero si trova in una directory fornita in PATH env var), args [0] sarà solo il nome dell'eseguibile, senza il percorso.
Kevin

@ Kevin: tu (e altri) hai ragione, questa è una soluzione semplice, per piccoli strumenti, che funzionano ~ 95% dei casi. Per un software serio, un file di configurazione e / o una variabile di ambiente è probabilmente migliore. Inoltre, questa necessità implica di solito un design non molto buono (o addirittura sbagliato).
Adrian Maire


1

Quanto segue funziona come una soluzione rapida e sporca, ma nota che è ben lungi dall'essere infallibile:

#include <iostream>

using namespace std ;

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

17
Ho visto su altre domande SO che questo non funziona sempre e che argv [0] può contenere il percorso assoluto dell'eseguibile, solo il nome del file dell'eseguibile o qualsiasi altra spazzatura.
Ben Hymers

7
Non ci si dovrebbe mai fidare di argv [0] se stanno tentando di aprire "file di supporto" o simili. Argv è soggetto a modifiche e qualsiasi chiamante malvagio può modificare il valore di questo. Evita a meno che tu non lo stia usando per la registrazione, ecc., NON per costruire percorsi usati per aprire i file.
Qix - MONICA È STATA MALTRATTATA

questo non funziona su Windows. argv [0] non avrà il percorso completo. Solo il file .exe. Per favore, non provare in una shell bash, provalo in questa console standard e cout << argv [0] per riprodurre.
Freddy Martinez Garcia

@FreddyMartinezGarcia Beh, l'avrei testato in Windows, quindi YMMV. È tutto ciò che è stato utilizzato per avviare il codice. Se hai l'eseguibile nel CWD, sicuramente otterrai solo il nome del file.
Clifford

0

Nel caso in cui sia necessario gestire i percorsi Unicode per 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

Per Windows, hai il problema di come rimuovere l'eseguibile dal risultato di GetModuleFileName(). La chiamata API di Windows PathRemoveFileSpec()che Nate ha utilizzato a tale scopo nella sua risposta è cambiata tra Windows 8 e i suoi predecessori. Quindi come rimanere compatibili con entrambi e al sicuro? Fortunatamente, c'è C ++ 17 (o Boost, se stai usando un compilatore più vecchio). Lo faccio:

#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

Come altri menzionati, argv[0]è una soluzione abbastanza carina, a condizione che la piattaforma passi effettivamente il percorso eseguibile, che sicuramente non è meno probabile del sistema operativo Windows (dove WinAPI può aiutare a trovare il percorso eseguibile). Se vuoi rimuovere la stringa per includere solo il percorso della directory in cui risiede l'eseguibile, allora usare quel percorso per trovare altri file dell'applicazione (come le risorse di gioco se il tuo programma è un gioco) va perfettamente bene, poiché l'apertura dei file è relativa a la directory di lavoro o, se fornita, la root.


0

Questo è ciò con cui sono finito

Il file di intestazione ha questo aspetto:

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

}

Implementazione


#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

La libreria SDL2 ( https://www.libsdl.org/ ) ha due funzioni implementate su un ampio spettro di piattaforme:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Quindi se non vuoi reinventare la ruota ... purtroppo significa includere l'intera libreria, sebbene abbia una licenza abbastanza permissiva e si potrebbe anche copiare il codice. Inoltre, fornisce molte altre funzionalità multipiattaforma.


0

Questo è probabilmente il modo più naturale per farlo, coprendo la maggior parte delle principali piattaforme desktop. Non ne sono sicuro, ma credo che dovrebbe funzionare con tutti i BSD, non solo con FreeBSD, se cambi il controllo delle macro della piattaforma per coprirli tutti. Se dovessi mai riuscire a installare Solaris, mi assicurerò di aggiungere quella piattaforma all'elenco supportato.

Offre il supporto completo di UTF-8 su Windows, cosa che non a tutti interessa abbastanza da arrivare a tanto.

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

Ciò consente di ottenere il percorso completo dell'eseguibile di praticamente qualsiasi ID di processo, tranne che su Windows ci sono alcuni processi con attributi di sicurezza che semplicemente non lo consentono, quindi wysiwyg, questa soluzione non è perfetta.

Per rispondere a ciò che la domanda stava chiedendo in modo più preciso, puoi fare questo:

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

Costruisci la struttura di file sopra con questo comando:

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

Per scaricare una copia dei file sopra elencati:

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

Per una maggiore bontà correlata al processo multipiattaforma:

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

Vedere il file readme per un elenco della maggior parte delle funzioni incluse.


0

Se si utilizza C ++ 17, è possibile eseguire le seguenti operazioni per ottenere il percorso dell'eseguibile.

#include <filesystem>

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

La risposta sopra è stata testata su Debian 10 usando G ++ 9.3.0


Nota che questo funzionerà solo se / proc / self / exe esiste ed è accessibile. Probabilmente dovresti controllare se questo è il caso.
Zrin

-1

A partire da C ++ 17:

Assicurati di includere il filesystem std.

#include <filesystem>

e ora puoi farlo.

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

boost filesystem divenne parte della libreria standard.

se non riesci a trovarlo prova a guardare sotto:

std::experimental::filesystem

10
Questo non è il percorso del binario, è la directory di lavoro corrente.
Zitrax

-2

Questa era la mia soluzione in Windows. Si chiama così:

std::wstring sResult = GetPathOfEXE(64);

Dove 64 è la dimensione minima che pensi che sarà il percorso. GetPathOfEXE chiama se stesso in modo ricorsivo, raddoppiando la dimensione del buffer ogni volta finché non ottiene un buffer sufficientemente grande per ottenere l'intero percorso senza troncamento.

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

Qual è il motivo per l'utilizzo newe il (sbagliato) delete? Se avessi usato a std::vector, il tuo codice non avrebbe mostrato un comportamento indefinito.
Rilevabile

Inoltre, GetModuleFileNameWnon imposta l'ultimo codice di errore in caso di successo. Quel codice è rotto in tanti modi. Non usare se ti capita di imbatterti in questo.
Rilevabile il

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

2
È solo per Windows e utilizza MFC, quindi abbastanza lontano dall'essere multipiattaforma, mi dispiace!
Ben Hymers

1
Questo non è nemmeno il modo in cui Windows lo fa. Dai uno sguardo invece alle PathRemoveFileSpec()funzioni correlate.
Remy Lebeau

-4

in Unix (incluso Linux) prova "quale", in Windows prova "dove".

#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

Questo metodo funziona sia per Windows che per 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
Ciò restituisce la directory di lavoro corrente, non il percorso dell'eseguibile che potrebbe non essere la stessa cosa.
Dave Durbin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.