Obtener la ruta del ejecutable

114

Sé que esta pregunta se ha hecho antes, pero todavía no he visto una respuesta satisfactoria o un "no, esto no se puede hacer" definitivo, ¡así que volveré a preguntar!

Todo lo que quiero hacer es obtener la ruta al ejecutable que se está ejecutando actualmente, ya sea como una ruta absoluta o en relación con el lugar desde donde se invoca el ejecutable, de forma independiente de la plataforma. Pensé que boost :: filesystem :: initial_path era la respuesta a mis problemas, pero parece que solo maneja la parte 'independiente de la plataforma' de la pregunta: todavía devuelve la ruta desde la que se invocó la aplicación.

Para un poco de fondo, este es un juego que usa Ogre, que estoy tratando de perfilar usando Very Sleepy, que ejecuta el ejecutable de destino desde su propio directorio, por lo que, por supuesto, al cargar el juego no encuentra archivos de configuración, etc. y se bloquea rápidamente . Quiero poder pasarle una ruta absoluta a los archivos de configuración, que sé que siempre estarán junto al ejecutable. Lo mismo ocurre con la depuración en Visual Studio: me gustaría poder ejecutar $ (TargetPath) sin tener que configurar el directorio de trabajo.

Ben Hymers
fuente
3
stackoverflow.com/questions/1023306/… y otros
dmckee --- ex-moderador gatito
9
Tenga en cuenta que es imposible probar la ausencia de una respuesta, por lo que no puede obtener un NO definitivo . Estaré feliz de darle un NO autorizado :)
MSalters
" al cargar, el juego no encuentra archivos de configuración, etc. ", por lo que el juego busca archivos de configuración en el directorio actual? Esa es una mala idea y potencialmente una vulnerabilidad de seguridad. Los archivos de configuración deben almacenarse en una ubicación estándar.
Curioso
1
Publiqué una respuesta aquí a una pregunta relacionada que también responde a la suya, trabajando en todas las plataformas usando boost
jtbr

Respuestas:

86

No hay una forma multiplataforma que yo sepa.

Para Linux: readlink / proc / self / exe

Windows: GetModuleFileName

Pato
fuente
9
La independencia de la plataforma es simplemente una cuestión de ocultar la dependencia de la plataforma. En este caso, usar las macros predefinidas del sistema operativo detalladas en predef.sourceforge.net/preos.html para seleccionar el método es sencillo.
Clifford
4
Entonces, ¿es esto lo que todos hacen cuando quieren encontrar la ruta del ejecutable en C ++? Esperaba que algo tan simple como esto ya estuviera implementado en una biblioteca como boost.
Ben Hymers
2
@curiousguy No estoy seguro de entenderte; Estoy bastante seguro de que ese es el objetivo de esta pregunta :)
Ben Hymers
6
@curiousguy: le gustaría hacerlo si, por ejemplo, su programa puede instalarse en un directorio que elija el usuario. Necesita poder encontrar su ejecutable y sus archivos de soporte de alguna manera.
greyfade
1
@Duck, ¿actualizarías tu respuesta con un enlace a mi biblioteca? Mi comentario está enterrado en la lista.
Gregory Pakosz
35

La función boost :: dll :: program_location es uno de los mejores métodos multiplataforma para obtener la ruta del ejecutable en ejecución que conozco. La biblioteca DLL se agregó a Boost en la versión 1.61.0.

La siguiente es mi solución. Lo he probado en Windows, Mac OS X, Solaris, Free BSD y GNU / Linux.

Requiere Boost 1.55.0 o superior. Utiliza la biblioteca Boost.Filesystem directamente y la biblioteca Boost.Locale y la biblioteca Boost.System indirectamente.

src / ruta_ejecutable.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 / ruta_ejecutable_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;
}

}
}

incluir / boost / ruta_ejecutable.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_

incluir / boost / detail / ruta_ejecutable_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_

Tengo un proyecto completo, que incluye una aplicación de prueba y archivos de compilación de CMake disponibles en SnKOpen - / cpp / ejecutable_path / trunk . Esta versión es más completa que la versión que proporcioné aquí. También es compatible con más plataformas.

He probado la aplicación en todos los sistemas operativos compatibles en los siguientes cuatro escenarios.

  1. Ruta relativa, ejecutable en el directorio actual: es decir, ./executable_path_test
  2. Ruta relativa, ejecutable en otro directorio: es decir ./build/executable_path_test
  3. Ruta completa: es decir, / algunos / dir / ejecutable_path_test
  4. Ejecutable en ruta, solo nombre de archivo: es decir, ejecutable_path_test

En los cuatro escenarios, las funciones ejecutable_path y ejecutable_path_fallback funcionan y devuelven los mismos resultados.

Notas

Ésta es una respuesta actualizada a esta pregunta. Actualicé la respuesta para tener en cuenta los comentarios y sugerencias de los usuarios. También agregué un enlace a un proyecto en mi Repositorio SVN.

Ben Key
fuente
1
Parece una solución muy completa con alternativas razonables. +1! Sin embargo, una pregunta: ¿Tendría sentido reemplazar los búferes de caracteres fijos [1024] con algo como un vector <char> que se puede cambiar de tamaño si la ruta excede el tamaño inicial?
Daniel Wolf
Si. Esa es una excelente sugerencia. Por supuesto, sería necesario realizar algunos cambios adicionales, como comprobar si hay errores, cambiar el tamaño del búfer y volver a intentarlo.
Ben Key
1
Creo que la alternativa no es correcta. argv[0]también puede ser solo un nombre ejecutable, en cuyo caso sería necesario buscarlo en los PATHsistemas * nix.
Michał Górny
1
Intenté usar esto. pero necesita impulso, ¿correcto? Pensé que era independiente
manatttta
1
Me tenías en "boost :: dll :: program_location"
Thomas
31

De esta manera usa boost + argv. Mencionaste que esto puede no ser multiplataforma porque puede o no incluir el nombre del ejecutable. Bueno, el siguiente código debería solucionarlo.

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

El siguiente código obtiene el directorio de trabajo actual que puede hacer lo que necesita

#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 Acabo de darme cuenta de que basename() estaba en desuso, así que tuve que cambiar a.stem()

Ryu
fuente
Stem parece darme solo el ejecutable menos la ruta y la extensión en Windows, pero ese es un punto menor. Lo que me gustaría saber es cómo funciona esto para evitar que argv [0] sea posiblemente incorrecto. Me funciona probando en Windows, pero luego argv [0] en realidad se pasa como la ruta absoluta del ejecutable, lo que hace que el trabajo de system_complete sea bastante fácil :)
Ben Hymers
1
No, no necesita el directorio de trabajo. y NO argv no ayuda. ¿Qué haces cuando argv contiene solo el nombre del ejecutable? ¿Qué hacer cuando se invoca el programa a través de un enlace simbólico?
Ichthyo
4
"// Sin nombre de archivo" - quieres .parent_path(), no .stem(), ¿no?
Claudiu
2
Esto no parece funcionar en mi plataforma (macOS El Capitan). En su lugar, obtengo el directorio de trabajo actual. Además, como @Claudiudije, creo que debería serlo .parent_path().
samvv
20

No estoy seguro de Linux, pero prueba esto para 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
fuente
3
Tenga en cuenta que debe usar WCHAR ownPth.., envuelto alrededor de a #ifdef UNICODEen caso de que se compile con soporte Unicode. Si no es así, utilice el código proporcionado.
Dr1Ku
1
solo para que conste, solo estoy teniendo un caso divertido en el que GetModuleDirectory devuelve una ruta con las partes "..", como si estuviera tomando la cadena pura en bruto de la línea de comandos jajaja. de hecho, en este caso, Visual Studio está iniciando el proceso y ... es parte de la ruta de depuración. algo como $ (projectDir) ../ some.exe Usé PathCanonicalize de Shwlib pero es necesario vincularlo con esta lib. esto puede no ser deseable.
v.oddou
1
También recomendaría usar TCHAR para ownPath en lugar de char. Pero buena respuesta de todos modos.
anhoppe
¿Es posible que esto falle? Parece poco probable de un vistazo ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck
1
Si el primer parámetro de GetModuleFileName es NULL, recupera la ruta del archivo ejecutable del proceso actual.
lsalamon
12

Para ventanas:

GetModuleFileName - devuelve la ruta exe + nombre de archivo exe

Para eliminar el nombre de archivo
PathRemoveFileSpec

Sivabalan
fuente
1
Docs en cuenta para PathRemoveFileSpec: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs
12

C ++ 17, windows, unicode, usando la nueva api del sistema de archivos:

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

Sospecho que esta solución debería ser portátil, pero no sé cómo se implementa Unicode en otros sistemas operativos.

Débilly_canonical solo es necesario si utiliza como directorio de salida las referencias de la carpeta superior ('..') para simplificar la ruta. Si no lo usa, elimínelo.

Si está operando desde la biblioteca de enlaces dinámicos (.dll /.so), entonces es posible que no tenga argv, entonces puede considerar la siguiente solución:

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
fuente
Los protectores dentro del encabezado no son pruebas adecuadas para detectar la presencia de un sistema de archivos. cppreference muestra que el valor de la macro de prueba de características se define en el encabezado del sistema de archivos, por lo tanto, no funciona para probar antes de incluir. __has_include () es una mejor prueba estándar aquí.
Meteorhead
8

QT proporciona esto con la abstracción del sistema operativo como QCoreApplication :: applicationDirPath ()

ted
fuente
Consiguiendo con esto: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. ¿Alguna idea de cómo solucionar eso?
GuySoft
@GuySoft: Simplemente cree una instancia de QCoreApplicationlike so QApplication application(argc, argv);(haga esto en su main(argc, argv), y asegúrese de no modificar el argc/argv, ya que estos deben permanecer válidos durante la vida útil de QCoreApplication (consulte la documentación )
ted
5

Esta es una forma específica de Windows, pero es al menos la mitad de su respuesta.

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

Sugeriría usar la detección de plataforma como directivas de preprocesador para cambiar la implementación de una función contenedora que llama GetThisPath a cada plataforma.

Nate
fuente
3

Usando args [0] y buscando '/' (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;
}

EDITADO: Si '/' no existe, pos == - 1 para que el resultado sea correcto.

Adrian Maire
fuente
¿Qué pasa si '/' no está presente en la ruta? No hay verificación de ese caso y creo que es bastante probable: Windows usará barras invertidas y es args[0]posible que en realidad no sea una ruta.
Ben Hymers
Si '/' no existe, rfind devuelve -1, entonces "ruta" = aux.substr (0,0) y "nombre" = aux.substr (0): el resultado es correcto. En relación con Windows, tiene razón, '/' debe cambiarse a '\\', cambiaré para permitir Windows también. También he probado los nombres de archivo con '/', pero este último está codificado y no crea problemas.
Adrian Maire
1
Es más la parte de args[0]no ser necesariamente la ruta ejecutable lo que me molesta. Sin embargo, gracias por arreglar su respuesta para Windows :)
Ben Hymers
1
Si el comando se ejecuta sin dar la ruta (es decir, se encuentra en un directorio dado en la var. Env PATH), args [0] será solo el nombre del ejecutable, sin la ruta.
Kevin
@Kevin: usted (y otros) tienen razón, esta es una solución simple, para pequeñas herramientas, que funcionan ~ 95% de los casos. Para software serio, probablemente sea mejor un archivo de configuración y / o una variable de entorno. Además, esta necesidad suele implicar un diseño no muy bueno (o incluso incorrecto).
Adrian Maire
1

Lo siguiente funciona como una solución rápida y sucia, pero tenga en cuenta que está lejos de ser infalible:

#include <iostream>

using namespace std ;

int main( int argc, char** argv)
{
    cout << argv[0] << endl ;
    return 0;
}
Clifford
fuente
17
He visto en otras preguntas de SO que esto no siempre funciona, y que argv [0] puede contener la ruta absoluta al ejecutable, solo el nombre de archivo del ejecutable o cualquier otra basura.
Ben Hymers
7
Nunca se debe confiar en argv [0] si están intentando abrir 'archivos de soporte' o similares. Argv está sujeto a cambios, y cualquier llamador que sea maligno puede cambiar el valor de esto. Evítelo a menos que lo esté usando para el registro, etc., NO para construir rutas utilizadas para abrir archivos.
Qix - MONICA FUE MALTRATADA
esto no funciona en Windows. argv [0] no tendrá la ruta completa. Solo el archivo .exe. Por favor, no intente en un shell bash, inténtelo en esta consola estándar y cout << argv [0] para reproducir.
Freddy Martinez Garcia
@FreddyMartinezGarcia Bueno, lo habría probado en Windows, entonces YMMV. Es lo que se utilizó para lanzar el código. Si tiene el ejecutable en el CWD, seguro que solo obtendrá el nombre del archivo.
Clifford
0

En caso de que necesite manejar rutas Unicode para 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
fuente
0

Para Windows, tiene el problema de cómo quitar el ejecutable del resultado de GetModuleFileName(). La llamada a la API de Windows PathRemoveFileSpec()que Nate usó para ese propósito en su respuesta cambió entre Windows 8 y sus predecesores. Entonces, ¿cómo seguir siendo compatible y seguro con ambos? Afortunadamente, existe C ++ 17 (o Boost, si está usando un compilador más antiguo). Hago esto:

#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
fuente
0

Como otros mencionaron, argv[0]es una solución bastante buena, siempre que la plataforma pase la ruta ejecutable, lo que seguramente no es menos probable que el sistema operativo Windows (donde WinAPI puede ayudar a encontrar la ruta ejecutable). Si desea quitar la cadena para incluir solo la ruta al directorio donde reside el ejecutable, entonces usar esa ruta para encontrar otros archivos de la aplicación (como activos del juego si su programa es un juego) está perfectamente bien, ya que abrir archivos es relativo a el directorio de trabajo o, si se proporciona, la raíz.

Kotauskas
fuente
0

Esto es con lo que terminé

El archivo de encabezado se ve así:

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

}

Implementación


#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
fuente
0

La biblioteca SDL2 ( https://www.libsdl.org/ ) tiene dos funciones implementadas en un amplio espectro de plataformas:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Entonces, si no desea reinventar la rueda ... lamentablemente, significa incluir toda la biblioteca, aunque tiene una licencia bastante permisiva y también se podría simplemente copiar el código. Además, proporciona muchas otras funciones multiplataforma.

usuario1050755
fuente
0

Esta es probablemente la forma más natural de hacerlo, al tiempo que cubre la mayoría de las principales plataformas de escritorio. No estoy seguro, pero creo que esto debería funcionar con todos los BSD, no solo con FreeBSD, si cambia la comprobación de macros de la plataforma para cubrirlos todos. Si alguna vez llego a instalar Solaris, me aseguraré de agregar esa plataforma a la lista compatible.

Cuenta con soporte completo para UTF-8 en Windows, que no a todos les importa lo suficiente como para llegar tan lejos.

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

Esto permite obtener la ruta completa al ejecutable de prácticamente cualquier ID de proceso, excepto que en Windows hay algunos procesos con atributos de seguridad que simplemente no lo permiten, así que wysiwyg, esta solución no es perfecta.

Para abordar lo que la pregunta preguntaba con mayor precisión, puede hacer lo siguiente:

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

Construya la estructura de archivo anterior con este 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

Para descargar una copia de los archivos enumerados anteriormente:

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

Para obtener más ventajas relacionadas con los procesos multiplataforma:

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

Consulte el archivo Léame para obtener una lista de la mayoría de las funciones incluidas.

Samuel Joseph Venable
fuente
0

Si usa C ++ 17, puede hacer lo siguiente para obtener la ruta al ejecutable.

#include <filesystem>

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

La respuesta anterior ha sido probada en Debian 10 usando G ++ 9.3.0

Scillman
fuente
Tenga en cuenta que esto solo funcionará si / proc / self / exe existe y es accesible. Probablemente debería comprobar si este es el caso.
Zrin
-1

A partir de C ++ 17:

Asegúrese de incluir el sistema de archivos std.

#include <filesystem>

y ahora puedes hacer esto.

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

El sistema de archivos boost se convirtió en parte de la lib estándar.

si no puede encontrarlo, intente buscar debajo:

std::experimental::filesystem
tomer zeitune
fuente
10
Esta no es la ruta del binario, es el directorio de trabajo actual.
Zitrax
-2

Esta fue mi solución en Windows. Se llama así:

std::wstring sResult = GetPathOfEXE(64);

Donde 64 es el tamaño mínimo que cree que tendrá la ruta. GetPathOfEXE se llama a sí mismo de forma recursiva, duplicando el tamaño del búfer cada vez hasta que obtiene un búfer lo suficientemente grande como para obtener la ruta completa sin truncamiento.

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
fuente
¿Cuál es la razón para usar newy el (incorrecto) delete? Si hubiera utilizado un std::vector, su código no habría mostrado un comportamiento indefinido.
Inspectable
Además, GetModuleFileNameWno establece el último código de error en caso de éxito. Ese código está roto de muchas maneras. No lo use si se encuentra con esto.
Inspectable
-3
char exePath[512];
CString strexePath;
GetModuleFileName(NULL,exePath,512);
strexePath.Format("%s",exePath);
strexePath = strexePath.Mid(0,strexePath.ReverseFind('\\'));
Vishwa
fuente
2
Eso es solo para Windows y usa MFC, así que está bastante lejos de ser multiplataforma, ¡lo siento!
Ben Hymers
1
Esta ni siquiera es la forma en que Windows lo hace. En su lugar, eche un vistazo a PathRemoveFileSpec()las funciones relacionadas.
Remy Lebeau
-4

en Unix (incluido Linux) intente 'cuál', en Windows intente 'dónde'.

#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
fuente
-4

Este método funciona tanto para Windows como para 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
fuente
2
Esto devuelve el directorio de trabajo actual, no la ruta al ejecutable, que puede no ser lo mismo.
Dave Durbin