Çalıştırılabilir dosyanın yolunu alın


115

Bu sorunun daha önce sorulduğunu biliyorum ama yine de tatmin edici bir yanıt veya kesin bir "hayır, bu yapılamaz" görmedim, bu yüzden tekrar soracağım!

Tek yapmak istediğim, şu anda çalışan yürütülebilir dosyanın yolunu, ya mutlak bir yol olarak ya da yürütülebilir dosyanın başlatıldığı yere göre platformdan bağımsız bir şekilde elde etmektir. Boost :: filesystem :: initial_path, sorunlarımın yanıtı olsa da, bu sorunun yalnızca 'platformdan bağımsız' kısmını ele alıyor gibi görünüyor - yine de uygulamanın çağrıldığı yolu döndürüyor.

Biraz arka plan için, bu Ogre kullanan bir oyundur, Hedef yürütülebilir dosyayı kendi dizininden çalıştıran Çok Uykulu kullanarak profil oluşturmaya çalışıyorum, bu yüzden tabii ki oyun yüklerken herhangi bir yapılandırma dosyası bulamaz ve hemen çöküyor. . Her zaman yürütülebilir dosyanın yanında yaşayacağını bildiğim yapılandırma dosyalarına mutlak bir yol iletebilmek istiyorum. Aynı şey Visual Studio'da hata ayıklama için de geçerli - çalışma dizinini ayarlamak zorunda kalmadan $ (TargetPath) 'ı çalıştırabilmek istiyorum.



9
Bir cevabın olmadığını kanıtlamanın imkansız olduğuna dikkat edin, bu nedenle kesin bir HAYIR alamazsınız . Size yetkili bir HAYIR vermekten mutluluk duyacağım :)
MSalters


" Yüklendiğinde oyun yapılandırma dosyası vb. bulmaz. " Böylece oyun geçerli dizinde yapılandırma dosyalarını arar mı? Bu kötü bir fikir ve potansiyel olarak bir güvenlik açığı. Yapılandırma dosyaları standart bir konumda saklanmalıdır.
curiousguy

1
Boost kullanarak platformlarda çalışarak, sizin de yanıtınızı veren ilgili bir soruya burada bir yanıt gönderdim
jtbr

Yanıtlar:


86

Bildiğim çapraz platform yolu yok.

Linux için: readlink / proc / self / exe

Windows: GetModuleFileName


9
Platform bağımsızlığı, platform bağımlılığını gizleme meselesidir. Bu durumda yöntemi seçmek için predef.sourceforge.net/preos.html adresinde ayrıntılı olarak açıklanan önceden tanımlanmış işletim sistemi makrolarının kullanılması basittir.
Clifford

4
Öyleyse, C ++ 'da yürütülebilir dosyanın yolunu bulmak isteyen herkesin yaptığı şey bu mu? Bu kadar basit bir şeyin, boost gibi bir kütüphanede zaten uygulanacağını umuyordum.
Ben Hymers

2
@curiousguy Seni anladığımdan emin değilim; Eminim bu sorunun tüm noktası budur :)
Ben Hymers

6
@curiousguy: Örneğin, programınız kullanıcının seçtiği bir dizine kurulabilirse, bunu yapmak istersiniz. Çalıştırılabilir dosyanızı ve destek dosyalarını bir şekilde
greyfade

1
@Duck cevabınızı kütüphaneme bir bağlantı ile günceller misiniz? Yorumum listenin altına gömüldü.
Gregory Pakosz

35

Boost :: dll :: program_location işlevi Bildiğim kadarıyla çalışan yürütülebilir yolunu almanın en iyi çapraz platform yöntemlerden biridir. DLL kitaplığı, 1.61.0 sürümünde Boost'a eklendi.

Aşağıdaki benim çözümüm. Windows, Mac OS X, Solaris, Free BSD ve GNU / Linux üzerinde test ettim.

Boost 1.55.0 veya üstü gerektirir . Kullandığı Boost.Filesystem kütüphanesi doğrudan ve Boost.Locale kütüphane ve Boost.System dolaylı kütüphane.

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 / ayrıntılı / 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;
}

}
}

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

/ Boost / ayrıntılı / executable_path_internals.hpp içerir

#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_

SnKOpen - / cpp / execable_path / trunk'da bulunan bir test uygulaması ve CMake derleme dosyaları da dahil olmak üzere eksiksiz bir projem var . Bu sürüm, burada sunduğum sürümden daha eksiksiz. Ayrıca daha fazla platformu destekliyor.

Uygulamayı, desteklenen tüm işletim sistemlerinde aşağıdaki dört senaryoda test ettim.

  1. Göreli yol, geçerli dizinde çalıştırılabilir: yani ./executable_path_test
  2. Göreli yol, başka bir dizinde çalıştırılabilir: yani ./build/executable_path_test
  3. Tam yol: yani / bir / dir / yürütülebilir_yol_test
  4. Yolda yürütülebilir, yalnızca dosya adı: yani yürütülebilir_yol_testi

Dört senaryonun tümünde, hem yürütülebilir_ yol hem de yürütülebilir_ yol_ geri dönüş işlevleri çalışır ve aynı sonuçları döndürür.

notlar

Bu, bu soruya güncellenmiş bir cevaptır. Yanıtı, kullanıcı yorumlarını ve önerilerini dikkate alacak şekilde güncelledim. Ayrıca SVN Depomdaki bir projeye bağlantı ekledim.


1
Bu, makul geri dönüşlerle çok eksiksiz bir çözüm gibi görünüyor. 1! Yine de bir soru: Sabit karakter [1024] tamponlarını, yol başlangıç ​​boyutunu aşarsa yeniden boyutlandırılabilen <char> vektörü gibi bir şeyle değiştirmek mantıklı olur mu?
Daniel Wolf

Evet. Bu mükemmel bir öneri. Elbette hataları kontrol etmek, arabelleği yeniden boyutlandırmak ve tekrar denemek gibi bazı ek değişikliklerin yapılması gerekecektir.
Ben Key

1
Geri dönüşün doğru olmadığını düşünüyorum. argv[0]aynı zamanda sadece çalıştırılabilir isim de olabilir, bu durumda PATH* nix sistemlerinde aranması gerekir .
Michał Górny

1
Bunu kullanmayı denedim. ama güçlendirilmesi gerekiyor, değil mi? Bunun bağımsız olduğunu düşündüm
manatttta

1
Beni "boost :: dll :: program_location"
Thomas

31

Bu şekilde boost + argv kullanılır. Yürütülebilir adı içerebileceği veya içermeyeceği için bunun çapraz platform olmayabileceğini söylediniz. Eh, aşağıdaki kod bunun etrafında çalışmalıdır.

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

Aşağıdaki kod, ihtiyacınız olanı yapabilecek mevcut çalışma dizinini alır.

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

Not basename() kullanımdan kaldırıldığını anladım, bu nedenle.stem()


stem, bana Windows'ta yalnızca yürütülebilir dosya eksi yol ve uzantı veriyor gibi görünüyor, ancak bu küçük bir nokta. Bilmek istediğim şey, argv [0] muhtemelen yanlış olduğunda bunun nasıl çalıştığıdır? Windows üzerinde test etmek için işe yarıyor, ancak sonra argv [0] aslında yürütülebilir dosyanın mutlak yolu olarak aktarılıyor, bu da system_complete'in işini oldukça kolaylaştırıyor :)
Ben Hymers

1
Hayır - çalışma dizinine ihtiyacı yok. ve NO argv yardımcı olmuyor. Argv yalnızca çalıştırılabilir adı içerdiğinde ne yaparsınız ? Program bir sembolik bağlantı aracılığıyla çağrıldığında ne yapmalı?
Ichthyo

4
"// Dosya adı olmadan" - istiyorsun .parent_path(), değil .stem()mi?
Claudiu

2
Bu, platformumda (macOS El Capitan) çalışmıyor gibi görünüyor. Bunun yerine mevcut çalışma dizinini alıyorum. Ayrıca, @Claudiudediğim gibi, olması gerektiğini düşünüyorum .parent_path().
samvv

20

Linux hakkında emin değilim, ancak bunu Windows için deneyin:

#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
Unicode desteği ile derleme yapılması durumunda WCHAR ownPth.., a'nın etrafına sarılmış olarak kullanılması gerektiğine dikkat #ifdef UNICODEedin. Değilse, verilen kodu kullanın.
Dr1Ku

1
sadece kayıt için, GetModuleDirectory'nin içinde ".." parçaları olan bir yol döndürdüğü komik bir durumla karşılaşıyorum, sanki lol komut satırından saf haldeki dizeyi alıyormuş gibi. aslında bu durumda visual studio süreci başlatıyor ve .. hata ayıklama yolunun bir parçası. $ (projectDir) ../ some.exe gibi bir şey Shwlib'deki PathCanonicalize'ı kullandım ama bu kütüphaneye bağlanmak gerekiyor. bu arzu edilmeyebilir.
v.oddou

1
Ayrıca char yerine ownPath için TCHAR'ı kullanmayı da tavsiye ederim. Ama yine de güzel cevap.
anhoppe

Bunun başarısız olması mümkün mü? Bir bakışta pek olası görünmüyor ...HMODULE hModule = GetModuleHandle(NULL);
kayleeFrye_onDeck

1
GetModuleFileName için ilk parametre NULL ise, geçerli işlemin yürütülebilir dosyasının yolunu alır.
lsalamon

12

Pencereler için:

GetModuleFileName - exe yolu + exe dosya adını döndürür

Dosya adını kaldırmak için
PathRemoveFileSpec


1
Dokümanlar PathRemoveFileSpec için not: This function is deprecated. We recommend the use of the PathCchRemoveFileSpec function in its place.
javs

12

C ++ 17, windows, unicode, dosya sistemi yeni api kullanarak:

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

Bu çözümün taşınabilir olması gerektiğinden şüpheleniyor, ancak unicode'un diğer işletim sistemlerinde nasıl uygulandığını bilmiyorum.

zayıf_canonical yalnızca, yolu basitleştirmek için Çıktı Dizini üst klasör başvuruları ('..') olarak kullanırsanız gereklidir. Kullanmıyorsanız - çıkarın.

Dinamik bağlantı kitaplığından (.dll /.so) çalıştırıyorsanız, argv'niz olmayabilir, o zaman aşağıdaki çözümü düşünebilirsiniz:

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
}

Başlığın içindeki korumalar, dosya sisteminin varlığı için uygun bir test değildir. cppreference, özellik test makrosunun değerinin dosya sistemi başlığının kendisinde tanımlandığını, dolayısıyla dahil etmeden önce test etmek için çalışmadığını gösterir. __has_include () burada daha iyi bir standart testtir.
Meteorhead

8

QT, bunu QCoreApplication :: applicationDirPath () olarak işletim sistemi soyutlamasıyla sağlar.


Bununla alınıyor: QCoreApplication::applicationDirPath: Please instantiate the QApplication object first. Bunu nasıl çözeceğine dair bir fikrin var mı?
GuySoft

@GuySoft: Basitçe bunun QCoreApplicationgibi bir örnek oluşturun QApplication application(argc, argv);(bunu kendi içinde yapın main(argc, argv)ve argc/argvQCoreApplication ömrü boyunca geçerli kalmaları gerektiğinden bunları değiştirmediğinizden emin olun ( belgeleri kontrol edin )
ted

5

Bu, Windows'a özgü bir yoldur, ancak cevabınızın en azından yarısıdır.

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

GetThisPathHer bir platformu çağıran bir sarmalayıcı işlevinin uygulamasını değiştirmek için önişlemci yönergeleri olarak platform algılamayı kullanmanızı öneririm .


3

Bağımsız değişkenler [0] kullanma ve '/' (veya '\\') araması:

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

DÜZENLENMİŞ: '/' yoksa, pos == - 1 yani sonuç doğrudur.


Yolda "/" yoksa ne olur? Bu durum kontrol args[0]edilmiyor ve bunun oldukça muhtemel olduğuna inanıyorum - Windows ters eğik çizgi kullanacak ve aslında bir yol olmayabilir.
Ben Hymers

'/' Yoksa, -1 döndürür, böylece "yol" = yardımcı.substr (0,0) ve "ad" = yardımcı.substr (0): sonuç doğrudur. Windows ile ilgili olarak haklısınız, '/' '\\' olarak değiştirilmelidir, pencerelere de izin vereceğim. Ayrıca '/' ile dosya adlarını da test ettim, ancak bu sonuncu kodlanmış ve sorun yaratmıyor.
Adrian Maire

1
Daha çok, args[0]beni rahatsız eden çalıştırılabilir yol olma zorunluluğu olmayan kısım . Yine de Windows için cevabınızı
düzelttiğiniz

1
Komut, yolu belirtmeden çalıştırılırsa (yani, PATH env değişkeninde verilen bir dizinde bulunarak bulunursa), args [0], yol olmadan çalıştırılabilir dosyanın adı olacaktır.
Kevin

@Kevin: siz (ve diğerleri) haklısınız, bu basit bir çözüm, vakaların ~% 95'inde çalışan küçük araçlar için. Ciddi bir yazılım için, bir yapılandırma dosyası ve / veya bir ortam değişkeni muhtemelen daha iyidir. Ayrıca, bu ihtiyaç genellikle çok iyi olmayan (hatta yanlış) bir tasarım anlamına gelir.
Adrian Maire


1

Aşağıdakiler hızlı ve kirli bir çözüm olarak çalışır, ancak kusursuz olmaktan uzak olduğunu unutmayın:

#include <iostream>

using namespace std ;

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

17
Diğer SO sorularında bunun her zaman işe yaramadığını ve argv [0] 'ın yürütülebilir dosyanın mutlak yolunu, yalnızca yürütülebilir dosyanın dosya adını veya başka bir çöpü içerebileceğini gördüm.
Ben Hymers

7
'Support files' veya benzerlerini açmaya çalışan kişi argv [0] 'a asla güvenmemelidir. Argv değişebilir ve kötü olan herhangi bir arayan bunun değerini değiştirebilir. Dosyaları açmak için kullanılan yolları oluşturmak için DEĞİL, günlük kaydı vb. İçin kullanmıyorsanız kaçının.
Qix - MONICA

bu Windows'ta çalışmaz. argv [0] tam yola sahip olmayacak. Yalnızca .exe dosyası. Lütfen, bir bash kabuğunu denemeyin, bu standart konsolda deneyin ve yeniden üretmek için << argv [0] komutunu verin.
Freddy Martinez Garcia

@FreddyMartinezGarcia Bunu Windows'ta test ederdim, yani YMMV. Kodu başlatmak için kullanılan şey buydu. CWD'de çalıştırılabilir dosya iseniz, sadece dosya adını alacağınızdan emin olun.
Clifford

0

Windows için unicode yollarını işlemeniz gerekirse:

#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

Windows için, yürütülebilir dosyanın sonucundan nasıl çıkarılacağıyla ilgili bir sorununuz var GetModuleFileName(). PathRemoveFileSpec()Nate'in cevabında bu amaçla kullandığı Windows API çağrısı , Windows 8 ve öncekiler arasında değişti. Öyleyse nasıl hem uyumlu hem de güvenli kalınır? Neyse ki, C ++ 17 (veya daha eski bir derleyici kullanıyorsanız, Boost) var. Bunu yapıyorum:

#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

Diğerlerinin de bahsettiği gibi, argv[0]platformun çalıştırılabilir yolu geçmesi şartıyla oldukça güzel bir çözümdür, bu kesinlikle işletim sisteminin Windows olduğundan daha az olası değildir (WinAPI'nin yürütülebilir yolu bulmasına yardımcı olabilir). Dizeyi, yalnızca yürütülebilir dosyanın bulunduğu dizinin yolunu içerecek şekilde çıkarmak istiyorsanız, diğer uygulama dosyalarını (programınız bir oyunsa oyun varlıkları gibi) bulmak için bu yolu kullanmak tamamen iyidir, çünkü dosyaları açmak çalışma dizini veya sağlanmışsa kök.


0

Bu benim son bulduğum şey

Başlık dosyası şuna benzer:

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

}

uygulama


#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

SDL2 ( https://www.libsdl.org/ ) kitaplığı, geniş bir platform yelpazesinde uygulanan iki işleve sahiptir:

  • SDL_GetBasePath
  • SDL_GetPrefPath

Yani tekerleği yeniden icat etmek istemiyorsanız ... ne yazık ki, bu, oldukça müsaadeli bir lisansa sahip olmasına rağmen, tüm kütüphaneyi dahil etmek anlamına gelir ve biri de kodu kopyalayabilir. Ayrıca, birçok başka platformlar arası işlevsellik sağlar.


0

Çoğu büyük masaüstü platformunu kapsarken, muhtemelen bunu yapmanın en doğal yolu budur. Emin değilim, ancak platform makro kontrolünü hepsini kapsayacak şekilde değiştirirseniz, bunun sadece FreeBSD'de değil, tüm BSD'lerde çalışması gerektiğine inanıyorum. Solaris'i kurmaya gelirsem, bu platformu desteklenenler listesine ekleyeceğimden emin olacağım.

Herkesin bu kadar ileri gitmek için yeterince umursamadığı Windows'ta tam UTF-8 desteği içerir.

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

Bu, hemen hemen her işlem kimliğinin yürütülebilir dosyasının tam yolunu elde etmeyi sağlar, ancak Windows dışında, güvenlik özniteliklerine sahip bazı işlemler buna izin vermez, bu nedenle wysiwyg, bu çözüm mükemmel değildir.

Sorunun neyi sorduğunu daha kesin bir şekilde ele almak için şunu yapabilirsiniz:

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

Yukarıdaki dosya yapısını şu komutla oluşturun:

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

Yukarıda listelenen dosyaların bir kopyasını indirmek için:

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

Platformlar arası süreçle ilgili daha fazla iyilik için:

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

Dahil edilen işlevlerin çoğunun listesi için benioku sayfasına bakın.


0

C ++ 17 kullanıyorsanız, yürütülebilir dosyanın yolunu almak için aşağıdakileri yapabilirsiniz.

#include <filesystem>

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

Yukarıdaki cevap Debian 10'da G ++ 9.3.0 kullanılarak test edilmiştir.


Bunun yalnızca / proc / self / exe varsa ve erişilebilirse çalışacağını unutmayın. Muhtemelen durumun bu olup olmadığını kontrol etmelisiniz.
Zrin

-1

C ++ 17'den itibaren:

Std dosya sistemini eklediğinizden emin olun.

#include <filesystem>

ve şimdi bunu yapabilirsiniz.

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

boost dosya sistemi standart kitaplığın bir parçası haline geldi.

bulamazsanız altına bakmayı deneyin:

std::experimental::filesystem

10
Bu ikilinin yolu değil, mevcut çalışma dizini.
Zitrax

-2

Bu, Windows'taki çözümümdü. Şöyle denir:

std::wstring sResult = GetPathOfEXE(64);

64, yolun olacağını düşündüğünüz minimum boyuttur. GetPathOfEXE kendini yinelemeli olarak çağırır, tüm yolu kesmeden almak için yeterince büyük bir arabellek alana kadar her seferinde arabelleğin boyutunu iki katına çıkarır.

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

Kullanmanın nedeni newve (yanlış) deletenedir? Bir kullansaydınız std::vector, kodunuz tanımsız bir davranış sergilemeyecekti.
IInspectable

Ayrıca GetModuleFileNameWbaşarılı olması durumunda son hata kodunu ayarlamaz. Bu kod birçok yönden kırılmıştır. Buna rastlarsanız kullanmayın.
2019, 21

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

2
Bu yalnızca Windows içindir ve MFC'yi kullanır, platformlar arası olmaktan çok uzaktır, üzgünüm!
Ben Hymers

1
Bu, bunu yapmanın Windows yolu bile değil. PathRemoveFileSpec()Bunun yerine ve ilgili işlevlere bir göz atın .
Remy Lebeau

-4

Unix'te (Linux dahil) 'hangisini' deneyin, Windows'ta 'nerede' deneyin.

#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

Bu yöntem hem Windows hem de Linux için çalışır:

#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
Bu, aynı şey olmayabilecek yürütülebilir dosyanın yolunu değil, geçerli çalışma dizinini döndürür.
Dave Durbin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.