获取可执行文件的路径

我知道以前也有人问过这个问题,但我仍然没有看到一个令人满意的答案,或者一个明确的“不,这不可能”,所以我再问一遍!

我所要做的就是以独立于平台的方式获取当前运行的可执行文件的路径,无论是作为绝对路径还是相对于从哪里调用可执行文件的路径。我认为 ost: : filessystem: : first _ path 是我的问题的答案,但它似乎只处理了问题的“平台独立”部分——它仍然返回应用程序被调用的路径。

作为一点背景知识,这是一个使用食人魔的游戏,我正在尝试使用非常瞌睡,它从自己的目录运行目标可执行文件,所以当然在加载时,游戏没有找到配置文件等,并迅速崩溃。我希望能够将其传递到配置文件的绝对路径,我知道这些配置文件将始终与可执行文件一起存在。在 Visual Studio 中进行调试也是如此——我希望能够运行 $(TargetPath)而不必设置工作目录。

255271 次浏览

下面是一个快速而肮脏的解决方案,但请注意,它远非万无一失:

#include <iostream>


using namespace std ;


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

这种方式使用了 ost + 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()

对于 Windows,可以使用 GetModuleFilename ()。
有关 Linux,请参见 DatenWolf 的 GitHub 仓库中的 BinRelocHttp://autopackage.org/docs/BinReloc/rel = “ nofollow noReferrer”> BinReloc (旧的,不存在的 URL) 镜像。

据我所知没有跨站台的路。

对于 Linux: 将 "/proc/self/exe"传递给 std::filesystem::canonicalreadlink

Windows: 将 NULL 作为模块句柄传递给 GetModuleFileName

窗户:

返回 exe 路径 + exe 文件名

删除文件名
PathRemoveFileSpec

我不太确定是不是 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 特有的方式,但它至少是你的答案的一半。

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的包装函式的实现。

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

在 Unix (包括 Linux)中尝试“ which”,在 Windows 中尝试“ 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;
}

使用 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,因此结果是正确的。

QT 为此提供了作为 QCoreApplication: : applicationDirPath ()的 OS 抽象

据我所知,Program _ location函数是获取正在运行的可执行文件路径的最佳跨平台方法之一。DLL 库是在1.61.0版本中添加到 Boost 的。

下面是我的解决方案,我已经在 Windows,Mac OS X,Solaris,Free BSD 和 GNU/Linux 上测试过了。

它需要 加油1.55.0或更高版本,直接使用 文件系统库库,间接使用 推进,现场库和 推进,系统库。

Src/Executive _ 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/Details/Executive _ path _ Internal als. 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/ost/Executive _ 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/ost/Details/Executive _ path _ Internal als. 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_

我有一个完整的项目,包括一个测试应用程序和 CMake 可用的构建文件在 SnKOpen-/cpp/Executive _ path/躯干。这个版本比我在这里提供的版本更完整。它还支持更多的平台。

我已经在以下四个场景中在所有受支持的操作系统上测试了该应用程序。

  1. 相对路径,在工作目录中可执行: 例如./Executive _ path _ test
  2. 相对路径,另一个目录中的可执行文件: 例如./build/Executive _ path _ test
  3. 完整路径: 例如/some/dir/Executive _ path _ test
  4. 在 path 中只有文件名是可执行的: 也就是可执行的 _ path _ test

在所有四种情况下,可执行路径函数和可执行路径函数都能正常工作并返回相同的结果。

笔记

这是对这个问题的最新回答。我更新了答案,以考虑用户的意见和建议。我还在我的 SVN 知识库中添加了一个到项目的链接。

对于 Windows,您遇到的问题是如何从 GetModuleFileName()的结果中剥离可执行文件。内特在回答中使用的 WindowsAPI 调用 PathRemoveFileSpec()在 Windows8和它的前辈之间发生了变化。那么如何保持兼容性和安全性呢?幸运的是,有 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.
}

这是我在 Windows 中的解决方案,叫做:

std::wstring sResult = GetPathOfEXE(64);

其中64是你认为路径的最小尺寸。GetPathOfEXE 递归地调用自己,每次将缓冲区的大小增加一倍,直到得到一个足够大的缓冲区,以便在不截断的情况下获取整个路径。

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

如果您需要处理 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;
}

正如其他人提到的,argv[0]是一个相当不错的解决方案,前提是平台实际上传递了可执行路径,这种可能性肯定不会低于 Windows 操作系统(WinAPI 可以帮助找到可执行路径)。如果你想去掉字符串,只包含可执行文件所在目录的路径,那么使用该路径来查找其他应用程序文件(比如游戏资产,如果你的程序是一个游戏)是完全可以的,因为打开的文件是相对于工作目录,或者,如果提供的话,根目录。

C + + 17,windows,unicode,使用文件系统 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;
}

(重要提示:使用 wmainwchar_t**-不要将 mainwchar_t**混合使用。对于 cmake 项目,使用 Unicode 使用 add_definitions(-DUNICODE -D_UNICODE))。

怀疑这个解决方案应该是可移植的,但不知道 Unicode 是如何在其他操作系统上实现的。

只有在使用 OutputDirectory 上层文件夹引用(’时,才需要厂商名称。.’)以简化路径。如果你不使用它-删除它。

如果使用的是动态链接库(。Dll/.所以) ,那么你可能没有 argv,然后你可以考虑下面的解决方案:

应用程序 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
}

从 C + + 17开始:

确保包含 std 文件系统。

#include <filesystem>

现在你可以这么做了。

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

升级文件系统成为标准库的一部分。

如果你找不到,试着往下看:

std::experimental::filesystem

这就是我的结局

头文件如下所示:

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


}


实施


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


}


SDL2(https://www.libsdl.org/)库具有两个在各种平台上实现的功能:

  • SDL _ GetBasePath
  • SDL _ GetPrefPath

因此,如果你不想重新发明轮子... 遗憾的是,这意味着包括整个库,虽然它有一个相当宽松的许可证,人们也可以只是复制代码。此外,它还提供了许多其他跨平台功能。

这可能是最自然的方式来做到这一点,同时涵盖了大多数主要的桌面平台。我不确定,但我相信这应该与所有的 BSD 的,而不仅仅是 FreeBSD,如果你改变平台宏检查,以涵盖所有他们。如果我有时间安装 Solaris,我一定会将该平台添加到支持列表中。

在 Windows 上支持完整的 UTF-8,并不是每个人都足够关心这一点。

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

这允许获得几乎所有进程 id 的可执行文件的完整路径,但是在 Windows 上有一些具有安全属性的进程是不允许的,所以 wysiwyg,这个解决方案并不完美。

为了更准确地回答这个问题,你可以这样做:

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

使用以下命令构建上面的文件结构:

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

下载上述档案的副本:

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

更多与跨平台流程相关的优点:

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

有关包含的大多数函数的列表,请参见自述文件。

如果使用 C + + 17,则可以执行以下操作来获取可执行文件的路径。

#include <filesystem>


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

上面的答案已经在 Debian 10上使用 G + + 9.3.0进行了测试

如果我的解决方案已经发布了,我没有读,但是在 linux 和 osx 上,你可以像下面这样读主函数中的0参数:

int main(int argument_count, char **argument_list) {
std::string currentWorkingDirectoryPath(argument_list[currentWorkingDirectory]);
std::size_t pos = currentWorkingDirectoryPath.rfind("/");      // position of "live" in str
currentWorkingDirectoryPath = currentWorkingDirectoryPath.substr (0, pos);

在 Senate _ list 的第一项中,可执行文件的名称被集成了,但是被上面的代码删除了。

下面是我的简单解决方案,它基于 这个解决方案并用 这个答案进行了修改,在 Windows 和 Linux 中都可以使用:

#include <string>
using namespace std;
#if defined(_WIN32)
#include <algorithm> // for transform() in get_exe_path()
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
#elif defined(__linux__)
#include <unistd.h> // for getting path of executable
#endif // Windows/Linux


string replace(const string& s, const string& from, const string& to) {
string r = s;
int p = 0;
while((p=(int)r.find(from, p))!=string::npos) {
r.replace(p, from.length(), to);
p += (int)to.length();
}
return r;
}
string get_exe_path() { // returns path where executable is located
string path = "";
#if defined(_WIN32)
wchar_t wc[260] = {0};
GetModuleFileNameW(NULL, wc, 260);
wstring ws(wc);
transform(ws.begin(), ws.end(), back_inserter(path), [](wchar_t c) { return (char)c; });
path = replace(path, "\\", "/");
#elif defined(__linux__)
char c[260];
int length = (int)readlink("/proc/self/exe", c, 260);
path = string(c, length>0 ? length : 0);
#endif // Windows/Linux
return path.substr(0, path.rfind('/')+1);
}

有几个答案推荐在 Windows 上使用 GetModuleFileName。这些答案有一些缺点,比如:

  • 该代码应该可以同时适用于 UNICODE 和 ANSI 版本
  • 路径可以比 MAX _ PATH 长
  • GetModuleFileName函数可能失败并返回0
  • GetModuleFileName可以返回一个相对的可执行名称,而不是一个完整的名称
  • GetModuleFileName可以返回类似于 C:\GIT-RE~1\TEST_G~1\test.exe的短路径

让我提供一个考虑到上述要点的改进版本:

#include <Windows.h>
#include <string>
#include <memory>
#include <iostream>


// Converts relative name like "..\test.exe" to its full form like "C:\project\test.exe".
std::basic_string<TCHAR> get_full_name(const TCHAR const* name)
{
// First we need to get a length of the full name string
const DWORD full_name_length{GetFullPathName(name, 0, NULL, NULL)};
if (full_name_length == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}


// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> full_name_buffer{new TCHAR[full_name_length]};
const DWORD res = GetFullPathName(name, full_name_length, full_name_buffer.get(), NULL);
if (res == 0) {
// GetFullPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}


// The full name has been successfully written to the buffer.
return std::basic_string<TCHAR>(full_name_buffer.get());
}


// Resolves short path like "C:\GIT-RE~1\TEST_G~1\test.exe" into its long form like "C:\git-repository\test_project\test.exe"
std::basic_string<TCHAR> get_long_name(const TCHAR const* name)
{
// First we need to get a length of the long name string
const DWORD long_name_length{GetLongPathName(name, 0, NULL)};
if (long_name_length == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}


// Now, when we know the length, we create a buffer with correct size and write the full name into it
std::unique_ptr<TCHAR[]> long_name_buffer{new TCHAR[long_name_length]};
const DWORD res = GetLongPathName(name, long_name_buffer.get(), long_name_length);
if (res == 0) {
// GetLongPathName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}


// The long name has been successfully written to the buffer.
return std::basic_string<TCHAR>(long_name_buffer.get());
}


std::basic_string<TCHAR> get_current_executable_full_name()
{
DWORD path_buffer_size = MAX_PATH; // we start with MAX_PATH because it is most likely that
// the path doesn't exceeds 260 characters
std::unique_ptr<TCHAR[]> path_buffer{new TCHAR[path_buffer_size]};


while (true) {
const auto bytes_written = GetModuleFileName(
NULL, path_buffer.get(), path_buffer_size);
const auto last_error = GetLastError();


if (bytes_written == 0) {
// GetModuleFileName call failed. Maybe you want to throw an exception.
return std::basic_string<TCHAR>{};
}


if (last_error == ERROR_INSUFFICIENT_BUFFER) {
// There is not enough space in our buffer to fit the path.
// We need to increase the buffer and try again.
path_buffer_size *= 2;
path_buffer.reset(new TCHAR[path_buffer_size]);
continue;
}


// GetModuleFileName has successfully written the executable name to the buffer.
// Now we need to convert it to a full long name
std::basic_string<TCHAR> full_name = get_full_name(path_buffer.get());
return get_long_name(full_name.c_str());
}
}


// Example of how this function can be used
int main()
{
#ifdef UNICODE
// If you use UNICODE version of WinApi
std::wstring exe_file_full_name = get_current_executable_full_name();
std::wstring exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of(L"\\"));
std::wcout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::wcout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#else
// If you use ANSI version of WinApi
std::string exe_file_full_name = get_current_executable_full_name();
std::string exe_folder_full_name = exe_file_full_name.substr(0, exe_file_full_name.find_last_of("\\"));
std::cout << exe_file_full_name << "\n"; // prints: C:\test_project\x64\Debug\test_program.exe
std::cout << exe_folder_full_name << "\n"; // prints: C:\test_project\x64\Debug
#endif
}