Obtenha o caminho do executável


115

Sei que essa pergunta já foi feita antes, mas ainda não vi uma resposta satisfatória, ou um "não, isso não pode ser feito" definitivo, então vou perguntar novamente!

Tudo o que quero fazer é obter o caminho para o executável em execução no momento, seja como um caminho absoluto ou relativo ao local de onde o executável é chamado, de forma independente da plataforma. Eu pensei que boost :: filesystem :: initial_path fosse a resposta para meus problemas, mas isso parece lidar apenas com a parte 'independente de plataforma' da questão - ele ainda retorna o caminho de onde o aplicativo foi chamado.

Para um pouco de fundo, este é um jogo usando Ogre, que estou tentando criar o perfil usando Very Sleepy, que executa o executável alvo de seu próprio diretório, então é claro que ao carregar o jogo não encontra arquivos de configuração etc. e trava imediatamente . Quero ser capaz de passar um caminho absoluto para os arquivos de configuração, que sei que sempre viverão junto com o executável. O mesmo se aplica à depuração no Visual Studio - gostaria de poder executar $ (TargetPath) sem precisar definir o diretório de trabalho.



9
Note que é impossível provar a ausência de resposta, portanto não se pode obter um NÃO definitivo . Terei todo o gosto em dar-lhe um NÃO
oficial


" ao carregar o jogo não encontra arquivos de configuração, etc. " então o jogo procura por arquivos de configuração no diretório atual? É uma má ideia e, potencialmente, uma vulnerabilidade de segurança. Os arquivos de configuração devem ser armazenados em um local padrão.
curioso

1
Eu postei uma resposta aqui para uma pergunta relacionada que também responde a sua, trabalhando em várias plataformas usando boost
jtbr

Respostas:


86

Não existe uma forma de plataforma cruzada que eu conheça.

Para Linux: readlink / proc / self / exe

Windows: GetModuleFileName


9
A independência da plataforma é simplesmente uma questão de ocultar a dependência da plataforma. Nesse caso, usar as macros de SO predefinidas detalhadas em predef.sourceforge.net/preos.html para selecionar o método é simples.
Clifford,

4
Então, é isso que todo mundo faz sempre que deseja encontrar o caminho do executável em C ++? Eu esperava algo tão simples quanto isso já estivesse implementado em uma biblioteca como o boost.
Ben Hymers,

2
@curiousguy Não tenho certeza se entendi você; Tenho certeza de que esse é o ponto principal desta pergunta :)
Ben Hymers

6
@curiousguy: você gostaria de fazer isso se, por exemplo, seu programa pudesse ser instalado em um diretório de escolha do usuário. Você precisa encontrar seu executável e seus arquivos de suporte de alguma forma.
greyfade

1
@Duck, você atualizaria sua resposta com um link para minha biblioteca? Meu comentário está enterrado na lista.
Gregory Pakosz

35

A função boost :: dll :: program_location é um dos melhores métodos de plataforma cruzada para obter o caminho do executável em execução que eu conheço. A biblioteca DLL foi adicionada ao Boost na versão 1.61.0.

O seguinte é minha solução. Eu testei no Windows, Mac OS X, Solaris, Free BSD e GNU / Linux.

Requer Boost 1.55.0 ou superior. Ele usa a biblioteca Boost.Filesystem diretamente eo Boost.Locale biblioteca e Boost.System indiretamente.

src / executable_path.cpp

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

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

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

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

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

namespace boost {

#if (BOOST_OS_CYGWIN || BOOST_OS_WINDOWS)

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

#elif (BOOST_OS_MACOS)

#  include <mach-o/dyld.h>

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

#elif (BOOST_OS_SOLARIS)

#  include <stdlib.h>

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

#elif (BOOST_OS_BSD)

#  include <sys/sysctl.h>

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

#elif (BOOST_OS_LINUX)

#  include <unistd.h>

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

#else

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

#endif

}

src / detail / executable_path_internals.cpp

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

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

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

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

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

namespace boost {
namespace detail {

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

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

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

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

}
}

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_

Eu tenho um projeto completo, incluindo um aplicativo de teste e arquivos de compilação CMake disponíveis em SnKOpen - / cpp / executable_path / trunk . Esta versão é mais completa do que a versão que forneci aqui. Também é compatível com mais plataformas.

Testei o aplicativo em todos os sistemas operacionais com suporte nos quatro cenários a seguir.

  1. Caminho relativo, executável no diretório atual: ie ./executable_path_test
  2. Caminho relativo, executável em outro diretório: ie ./build/executable_path_test
  3. Caminho completo: ou seja / algum / dir / executable_path_test
  4. Executável no caminho, nome do arquivo apenas: isto é, executable_path_test

Em todos os quatro cenários, as funções executable_path e executable_path_fallback funcionam e retornam os mesmos resultados.

Notas

Esta é uma resposta atualizada a esta pergunta. Atualizei a resposta para levar em consideração os comentários e sugestões dos usuários. Também adicionei um link para um projeto em meu repositório SVN.


1
Essa parece uma solução muito completa com alternativas razoáveis. +1! Uma pergunta, porém: faria sentido substituir os buffers char [1024] fixos por algo como um vetor <char> que pode ser redimensionado se o caminho exceder o tamanho inicial?
Daniel Wolf

Sim. Essa é uma excelente sugestão. É claro que algumas alterações adicionais precisariam ser feitas, como verificar se há erros, redimensionar o buffer e tentar novamente.
Ben Key

1
Acho que o fallback não é correto. argv[0]também pode ser apenas um nome executável, caso em que seria necessário procurá-lo nos PATHsistemas * nix.
Michał Górny

1
Eu tentei usar isso. mas precisa de reforço, correto? Eu pensei que era autônomo
manatttta

1
Você me teve em "boost :: dll :: program_location"
Thomas

31

Desta forma, usa boost + argv. Você mencionou que pode não ser uma plataforma cruzada porque pode ou não incluir o nome do executável. Bem, o código a seguir deve contornar isso.

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

O código a seguir obtém o diretório de trabalho atual que pode fazer o que você precisa

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

Observação Acabei de perceber que basename() foi descontinuado, então tive que mudar para.stem()


O stem parece me dar apenas o executável menos o caminho e a extensão no Windows, mas isso é um ponto secundário. O que eu gostaria de saber é como isso evita que argv [0] esteja possivelmente incorreto. Ele funciona para mim testando no Windows, mas argv [0] está realmente sendo passado como o caminho absoluto do executável, o que torna o trabalho do system_complete muito fácil :)
Ben Hymers

1
Não - ele não precisa do diretório de trabalho. e NO argv não ajuda. O que você faz quando argv contém apenas o nome do executável? O que fazer quando o programa foi invocado por meio de um link simbólico?
Ichthyo

4
"// Sem nome de arquivo" - você quer .parent_path(), não .stem(), não?
Claudiu

2
Isso não parece funcionar na minha plataforma (macOS El Capitan). Em vez disso, obtenho o diretório de trabalho atual. Além disso, como @Claudiudisse, acho que deveria ser .parent_path().
samvv

20

Não tenho certeza sobre o Linux, mas tente isso 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;
     }
}

3
Observe que deve-se usar WCHAR ownPth.., enrolado #ifdef UNICODEem caso de compilar com suporte a Unicode. Caso contrário, use o código fornecido.
Dr1Ku de

1
apenas para registro, estou tendo um caso engraçado onde GetModuleDirectory retorna um caminho com as partes ".." nele, como se estivesse pegando a string pura e crua da linha de comando lol. na verdade, neste caso, o visual studio está iniciando o processo e .. é parte do caminho de depuração. algo como $ (projectDir) ../ some.exe Eu usei PathCanonicalize do Shwlib, mas é necessário vincular a esta lib. isso pode não ser desejável.
v.oddou

1
Eu também recomendo usar TCHAR para o ownPath em vez de char. Mas boa resposta de qualquer maneira.
anhoppe

É mesmo possível que isso falhe? Parece improvável à primeira vista ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
Se o primeiro parâmetro de GetModuleFileName for NULL, ele recupera o caminho do arquivo executável do processo atual.
lsalamon

12

Para Windows:

GetModuleFileName - retorna o caminho exe + nome do arquivo exe

Para remover o nome do arquivo
PathRemoveFileSpec


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

12

C ++ 17, windows, unicode, usando sistema de arquivos new 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;
}

Suspeito que esta solução deve ser portátil, mas não sei como unicode é implementado em outros sistemas operacionais.

weakly_canonical é necessário apenas se você usar como referências de pasta superior do diretório de saída ('..') para simplificar o caminho. Se você não usar, remova-o.

Se você estiver operando a partir de uma biblioteca de vínculo dinâmico (.dll /.so), pode não ter argv, então você pode considerar a seguinte solução:

aplicação.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
}

Os guardas dentro do cabeçalho não são testes adequados para a presença do sistema de arquivos. cppreference mostra que o valor da macro de teste de recurso é definido no próprio cabeçalho do sistema de arquivos, portanto, não funciona testar antes de incluir. __has_include () é um teste padrão melhor aqui.
Meteorhead

8

QT fornece isso com abstração de sistema operacional como QCoreApplication :: applicationDirPath ()


Ficando com isso: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Alguma ideia de como resolver isso?
GuySoft

@GuySoft: Simplesmente crie uma instância QCoreApplicationsemelhante QApplication application(argc, argv);(faça isso em seu main(argc, argv)e certifique-se de não modificar o argc/argv, pois eles precisam permanecer válidos durante a vida útil do QCoreApplication (verifique a documentação )
ted

5

Esta é uma maneira específica do Windows, mas é pelo menos metade de sua resposta.

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

Eu sugeriria o uso de detecção de plataforma como diretivas de pré-processador para alterar a implementação de uma função de wrapper que chama GetThisPathcada plataforma.


3

Usando args [0] e procurando por '/' (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;
}

EDITADO: Se '/' não existe, pos == - 1 então o resultado está correto.


E se '/' não estiver presente no caminho? Não há verificação desse caso e acredito que seja bastante provável - o Windows usará barras invertidas e args[0]pode não ser realmente um caminho.
Ben Hymers

Se '/' não existe, rfind retorna -1, então "path" = aux.substr (0,0) e "name" = aux.substr (0): o resultado está correto. Em relação ao Windows, você tem razão, '/' deve ser alterado para '\\', vou mudar para permitir janelas também. Também testei nomes de arquivo com '/', mas este último é codificado e não cria problemas.
Adrian Maire

1
É mais a parte de args[0]não ser necessariamente o caminho executável que me incomoda. Obrigado por corrigir sua resposta para o Windows :)
Ben Hymers

1
Se o comando for executado sem fornecer o caminho (ou seja, for encontrado em um diretório fornecido em PATH env var), args [0] será apenas o nome do executável, sem o caminho.
Kevin

@Kevin: você (e outros) estão certos, esta é uma solução simples, para pequenas ferramentas, que funcionam em cerca de 95% dos casos. Para software sério, um arquivo de configuração e / ou uma variável de ambiente é provavelmente melhor. Além disso, essa necessidade geralmente implica um design não muito bom (ou até errado).
Adrian Maire


1

O seguinte funciona como uma solução rápida e suja, mas observe que está longe de ser à prova de falhas:

#include <iostream>

using namespace std ;

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

17
Já vi em outras questões do SO que isso nem sempre funciona e que argv [0] pode conter o caminho absoluto para o executável, apenas o nome do arquivo do executável ou qualquer outra porcaria.
Ben Hymers,

7
Nunca se deve confiar em argv [0] se estiver tentando abrir 'arquivos de suporte' ou algo semelhante. Argv está sujeito a alterações, e qualquer pessoa que fizer uma chamada que seja malvada pode alterar o valor disso. Evite, a menos que você esteja usando para registro, etc., NÃO para construir caminhos usados ​​para abrir arquivos.
Qix - MONICA ERA MISTREADA

isso não funciona no Windows. argv [0] não terá o caminho completo. Apenas o arquivo .exe. Por favor, não tente em um shell bash, tente neste console padrão e cout << argv [0] para reproduzir.
Freddy Martinez Garcia

@FreddyMartinezGarcia Bem, eu teria testado no Windows, então YMMV. É tudo o que foi usado para iniciar o código. Se você inserir o executável no CWD, obterá apenas o nome do arquivo.
Clifford

0

Caso você precise lidar com caminhos 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;
}

0

Para Windows, você tem o problema de como remover o executável do resultado de GetModuleFileName(). A chamada de API do Windows PathRemoveFileSpec()que Nate usou para esse propósito em sua resposta mudou entre o Windows 8 e seus predecessores. Então, como permanecer compatível com ambos e seguro? Felizmente, existe o C ++ 17 (ou Boost, se você estiver usando um compilador mais antigo). Eu faço isso:

#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

Como outros mencionaram, argv[0]é uma boa solução, desde que a plataforma realmente passe pelo caminho do executável, o que certamente não é menos provável do que o sistema operacional sendo Windows (onde o WinAPI pode ajudar a encontrar o caminho do executável). Se você quiser retirar a string para incluir apenas o caminho para o diretório onde o executável reside, usar esse caminho para encontrar outros arquivos de aplicativo (como ativos de jogo se o seu programa for um jogo) é perfeitamente adequado, já que abrir arquivos é relativo a o diretório de trabalho ou, se fornecido, a raiz.


0

Isso é o que eu terminei

O arquivo de cabeçalho tem a seguinte aparência:

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

}

Implementação


#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

A biblioteca SDL2 ( https://www.libsdl.org/ ) tem duas funções implementadas em um amplo espectro de plataformas:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Então, se você não quer reinventar a roda ... infelizmente, significa incluir a biblioteca inteira, embora ela tenha uma licença bastante permissiva e também se possa copiar o código. Além disso, oferece muitas outras funcionalidades de plataforma cruzada.


0

Esta é provavelmente a maneira mais natural de fazer isso, enquanto cobre a maioria das principais plataformas de desktop. Não tenho certeza, mas acredito que deve funcionar com todos os BSDs, não apenas com o FreeBSD, se você alterar a verificação da macro da plataforma para cobrir todos eles. Se eu conseguir instalar o Solaris, com certeza adicionarei essa plataforma à lista de suporte.

Possui suporte total a UTF-8 no Windows, o que nem todos se importam o suficiente para ir tão longe.

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

Isso permite obter o caminho completo para o executável de praticamente qualquer id de processo, exceto no Windows, existem alguns processos com atributos de segurança que simplesmente não permitem isso, então wysiwyg, esta solução não é perfeita.

Para responder ao que a pergunta estava perguntando com mais precisão, você pode fazer o seguinte:

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

Crie a estrutura de arquivo acima com 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 baixar uma cópia dos arquivos listados acima:

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

Para mais vantagens relacionadas ao processo de plataforma cruzada:

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

Consulte o leiame para obter uma lista da maioria das funções incluídas.


0

Se estiver usando C ++ 17, pode-se fazer o seguinte para obter o caminho para o executável.

#include <filesystem>

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

A resposta acima foi testada no Debian 10 usando G ++ 9.3.0


Observe que isso funcionará apenas se / proc / self / exe existir e estiver acessível. Você provavelmente deve verificar se este é o caso.
Zrin

-1

A partir de C ++ 17:

Certifique-se de incluir o sistema de arquivos std.

#include <filesystem>

e agora você pode fazer isso.

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

boost filesystem tornou-se parte da biblioteca padrão.

se você não conseguir encontrar, tente procurar em:

std::experimental::filesystem

10
Este não é o caminho do binário, é o diretório de trabalho atual.
Zitrax de

-2

Esta foi a minha solução no Windows. É chamado assim:

std::wstring sResult = GetPathOfEXE(64);

Onde 64 é o tamanho mínimo que você acha que será o caminho. GetPathOfEXE chama a si mesmo recursivamente, dobrando o tamanho do buffer a cada vez, até que obtenha um buffer grande o suficiente para obter o caminho inteiro sem truncamento.

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 é a razão de usar newe o (errado) delete? Se você tivesse usado um std::vector, seu código não teria exibido um comportamento indefinido.
Inspecionável de

Além disso, GetModuleFileNameWnão define o último código de erro em caso de sucesso. Esse código é quebrado de muitas maneiras. Não use se acontecer de você tropeçar nisso.
Inspecionável

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

2
Isso é apenas para Windows e usa MFC, muito longe de ser multiplataforma, desculpe!
Ben Hymers de

1
Essa não é nem a maneira do Windows de fazer isso. Em vez disso, dê uma olhada nas PathRemoveFileSpec()funções relacionadas.
Remy Lebeau

-4

no Unix (incluindo Linux) tente 'which', no Windows tente 'where'.

#include <stdio.h>

#define _UNIX

int main(int argc, char** argv)
{
        char cmd[128];
        char buf[128];
        FILE* fp = NULL;
#if defined(_UNIX)
        sprintf(cmd, "which %s > my.path", argv[0]);
#else
        sprintf(cmd, "where %s > my.path", argv[0]);
#endif
        system(cmd);
        fp = fopen("my.path", "r");
        fgets(buf, sizeof(buf), fp);
        fclose(fp);

        printf("full path: %s\n", buf);
        unlink("my.path");

        return 0;
}

-4

Este método funciona para Windows e 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
Isso retorna o diretório de trabalho atual, não o caminho para o executável, que pode não ser a mesma coisa.
Dave Durbin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.