この質問は以前に聞かれたことがあることは知っていますが、満足のいく答えや、「いいえ、これはできません」という決定的な答えはまだ見ていません。
私がしたいのは、現在実行中の実行可能ファイルへのパスを、絶対パスとして、または実行可能ファイルの呼び出し元からの相対パスとして、プラットフォームに依存しない方法で取得することです。私はboost :: filesystem :: initial_pathが私のトラブルへの答えでしたが、それは質問の「プラットフォームに依存しない」部分のみを処理するようです-それはまだアプリケーションが呼び出されたパスを返します。
少し背景を説明すると、これはOgreを使用したゲームです。VerySleepyを使用してプロファイルを作成しようとしています。VerySleepyは、独自のディレクトリからターゲット実行可能ファイルを実行します。 。構成ファイルへの絶対パスを渡すことができるようにしたいのですが、これは常に実行可能ファイルと一緒に存在します。 Visual Studioでのデバッグについても同じことが言えます。作業ディレクトリを設定せずに$(TargetPath)を実行できるようにしたいと思います。
boost :: dll :: program_location 関数は、私が知っている実行可能な実行可能ファイルのパスを取得する最良のクロスプラットフォームメソッドの1つです。 DLLライブラリーは、バージョン1.61.0でBoostに追加されました。
以下は私の解決策です。 Windows、Mac OS X、Solaris、Free BSD、GNU/Linuxでテストしました。
Boost 1.55.0以上が必要です。 Boost.Filesystemライブラリ を直接使用し、 Boost.Locale ライブラリと Boost.System ライブラリを間接的に使用します。
#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
}
#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;
}
}
}
#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_
#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/executable_path/trunk で利用可能なテストアプリケーションとCMakeビルドファイルを含む完全なプロジェクトがあります。このバージョンは、ここで提供したバージョンよりも完全です。また、より多くのプラットフォームをサポートしています。
次の4つのシナリオで、サポートされているすべてのオペレーティングシステムでアプリケーションをテストしました。
4つのシナリオすべてで、executable_path関数とexecutable_path_fallback関数の両方が機能し、同じ結果を返します。
これは、この質問に対する更新された回答です。ユーザーのコメントと提案を考慮して、回答を更新しました。 SVNリポジトリにプロジェクトへのリンクも追加しました。
この方法では、boost + argvを使用します。実行可能ファイル名が含まれている場合と含まれていない場合があるため、これはクロスプラットフォームではない可能性があります。次のコードはそれを回避するはずです。
#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;
}
次のコードは、現在の作業ディレクトリを取得します。これは必要なことを行う場合があります
#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;
}
注basename(
)が非推奨になったため、.stem()
に切り替える必要があることに気付きました。
Linuxについてはわかりませんが、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;
}
}
Windowsの場合:
GetModuleFileName
-exeパス+ exeファイル名を返します
ファイル名を削除するにはPathRemoveFileSpec
C++ 17、Windows、Unicode、ファイルシステムの新しい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;
}
このソリューションは移植性があると思われますが、Unicodeが他のOSにどのように実装されているかはわかりません。
weakly_canonicalは、出力ディレクトリとして上位フォルダー参照( '..')を使用してパスを簡略化する場合にのみ必要です。使用しない場合は削除してください。
ダイナミックリンクライブラリ(.dll /.so)から操作している場合は、argvがない可能性があるため、次の解決策を検討できます。
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
}
これはWindows固有の方法ですが、答えの少なくとも半分です。
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);
プラットフォーム検出をプリプロセッサディレクティブとして使用して、各プラットフォームのGetThisPath
を呼び出すラッパー関数の実装を変更することをお勧めします。
QTは、これにOS抽象化を提供します QCoreApplication :: applicationDirPath()
Windowsの場合、GetModuleFilename()を使用できます。
Linuxについては BinReloc(古い、無効なURL) datenwolfのGitHubリポジトリのBinReloc のミラー。
Args [0]を使用して '/'(または '\\')を探します:
#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;
}
編集:「/」が存在しない場合、pos ==-1であるため、結果は正しい。
以下は、迅速で汚い解決策として機能しますが、絶対確実なものではないことに注意してください。
#include <iostream>
using namespace std ;
int main( int argc, char** argv)
{
cout << argv[0] << endl ;
return 0;
}
WindowsのUnicodeパスを処理する必要がある場合:
#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;
}
Windowsの場合、GetModuleFileName()
の結果から実行可能ファイルを削除する方法に問題があります。 Nateが彼の答えでその目的に使用したWindows API呼び出しPathRemoveFileSpec()
は、Windows 8とその前身との間で変更されました。それで、両方の互換性と安全性を維持する方法は?幸いなことに、C++ 17(または、古いコンパイラを使用している場合はBoost)があります。私はこれをします:
#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.
}
C++ 17以降:
必ずstdファイルシステムを含めてください。
#include <filesystem>
そして今、あなたはこれを行うことができます。
std::filesystem::current_path().string()
Boostファイルシステムは標準ライブラリの一部になりました。
あなたがそれを見つけることができない場合は、下を見てみてください:
std::experimental::filesystem
他の人が述べたように、プラットフォームが実際に実行可能パスを渡す場合、argv[0]
は非常に素晴らしいソリューションです。これは、WindowsであるOS(WinAPIが実行可能パスを見つけるのを助ける)よりも確実ではありません。実行可能ファイルが存在するディレクトリへのパスのみを含むように文字列を削除する場合、ファイルを開くのは相対的であるため、そのパスを使用して他のアプリケーションファイル(プログラムがゲームの場合はゲームアセットなど)を見つけることは完全に問題ありません作業ディレクトリ、または提供されている場合はルート。