【问题标题】:Finding path of execution of C++ program寻找C++程序的执行路径
【发布时间】:2021-01-03 09:43:08
【问题描述】:

在我正在工作的 C++ 命令行程序中,我需要找到执行路径,即调用 .exe 文件的路径。例如,如果文件存储在D:\stored\location\,但它是从其他目录调用的,例如D:\executed\here\,我应该将D:\executed\here\ 作为路径。 命令提示符应该看起来像这样:-

D:\executed\here> D:\stored\location\program_name.exe getpath
Path of execution is : D:\executed\here

我尝试按照 herehere 的说明使用 GetModuleFileName,但得到了 \stored\here

有没有跨平台的方式来查找“执行路径”?

注意:- 我正在使用 VSCode。

编辑:编译器版本为:g++ (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 8.1.0 旁注:- 由于某种原因,在包含 #include<filesystem> 之后,std::filesystem 显示错误:'std::filesystem' has not been declared",所以我无法使用 filesystem.h

【问题讨论】:

  • 您似乎想要的是当前的working directory。快速搜索应该会告诉您获取它所需的特定于 Windows 的功能。
  • 至于std::filesystem 错误,请将其作为不同的问题发布。记得添加minimal reproducible example
  • 关于std::filesystem:它是在 C++17 中引入的。因此,您可能必须在编译器中使用-std=c++17 强制执行此操作。几天前我顺便解决了您的问题(使用与您相同的 Q/As),我还注意到我必须在 Linux (g++ 8.2) 上与-lstdc++fs 链接。在 VS2019 上,它开箱即用(当然是 /std:c++17)。
  • 我想知道的是:如果std::filesystem 根本不可用,#include<filesystem> 应该事先失败...
  • 如果您的 g++ 编译器是 g++ 8.1,那么您很有可能已经支持 C++17。 (SO: Why does GCC not seem to have the filesystem standard library?)。也许,你有类似的问题,就像我在我的 Debian inst 上提到的 g++ 8.2 一样。

标签: c++ path


【解决方案1】:

C++11 答案

出于好奇,我将 MinGW g++ 安装到我的 git-bash 中(在 How to install gcc in Git Bash (Windows). 这立即奏效,但很快就出现了第一个糟糕的惊喜:

ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath
$ which g++
/d/MinGW/bin/g++

ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath
$ g++ --version
g++.exe (MinGW.org GCC-6.3.0-1) 6.3.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

哦,哎呀! g++ 6.3。这解释了std::filesystem 的 OP 问题(我也得到了他们的支持)。

所以,我修改了 other answer 以将其反向移植到 C++11。

我的 C++11 符合 MCVE:

// Declaration (Header):

// standard C++ header:
#include <string>

// returns file path of this executable
std::string getExecPath();

/**************************************************************************/

// Definition (C++ Source):

// standard C++ header:
#include <cstring>
#include <codecvt>
#include <locale>

// OS header:
#ifdef _WIN32 // Is this Windows?
#include <windows.h>
#else // (not) _WIN32 // Then it's hopefully Linux.
#include <unistd.h>
#endif // _WIN32

std::string getExecPath()
{
#ifdef _WIN32 // Is this Windows?
  std::wstring path(1024, L'\0');
  const DWORD len
    = GetModuleFileNameW(NULL, &path[0], (DWORD)path.size());
  if (!len) return std::string(); // ERROR!
  path.resize(len);
  std::wstring_convert<std::codecvt_utf8<wchar_t>> convU16ToU8;
  return convU16ToU8.to_bytes(path);
#else // (not) _WIN32 // Then it's hopefully Linux.
  std::string path(1024, '\0');
  ssize_t len
    = readlink("/proc/self/exe", &path[0], path.size());
  if (len < 0) return std::string(); // ERROR!
  path.resize(len);
  return path;
#endif // _WIN32
}

std::string getCWD()
{
#ifdef _WIN32 // Is this Windows?
  std::wstring path(1024, L'\0');
  const DWORD len
    = GetCurrentDirectoryW(path.size(), &path[0]);
  if (!len) return std::string(); // ERROR!
  path.resize(len);
  std::wstring_convert<std::codecvt_utf8<wchar_t>> convU16ToU8;
  return convU16ToU8.to_bytes(path);
#else // (not) _WIN32 // Then it's hopefully Linux.
  std::string path(1024, '\0');
  if (!getcwd(&path[0], path.size())) return std::string(); // ERROR!
  size_t len = std::strlen(path.c_str());
  path.resize(len);
  return path;
#endif // _WIN32
}

/**************************************************************************/

// Test:

// standard C++ header:
#include <iostream>

int main()
{
  std::cout
    << "Exec. Path:   " << getExecPath() << '\n'
    << "Current Dir.: " << getCWD() << '\n';
}

MinGW 中的测试会话(g++ 6.3):

ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath/C++11
$ g++ -std=c++11 -O2 testExecPath.cc && mkdir test ; cd test ; ../a.exe
Exec. Path:   D:\ds32737\Entwicklung\tests\C++\execPath\C++11\a.exe
Current Dir.: D:\ds32737\Entwicklung\tests\C++\execPath\C++11\test

ds32737@lapeks415-017 MINGW64 /d/ds32737/Entwicklung/tests/C++/execPath/C++11/test
$

coliru 上测试:

g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && mkdir test ; cd test ; ../a.out
Exec. Path:   /tmp/1609678428-623235554/a.out
Current Dir.: /tmp/1609678428-623235554/test

注意:

我不知道这一点,因为我以前从未在 MinGW 中使用过 g++:
_WIN32 也在 MinGW 中定义,并且使用了相应的代码(并且必须使用)。
(之前,我使用 _MSC_VER 仅激活 MSVC 的 Windows 代码。 在这种情况下,我收到了一个编译器错误,因为 readlink() 不可用。 即使它本来是这样的,它也可能没有完成它在 Linux 上应该做的事情。)

【讨论】:

    【解决方案2】:

    C++17 答案

    MinGW 目前似乎与 g++ 6.3 一起提供,我认为它已经很老了。所以,我又写了一个Answer for C++11


    顺便说一句,我在
    SO: Get path of executable的帮助下解决了同样的问题
    几天前。

    我已将相关部分放入 MCVE:

    // Declaration (Header):
    
    // standard C++ header:
    #include <filesystem>
    
    // returns file path of this executable
    std::filesystem::path getExecPath();
    
    /**************************************************************************/
    
    // Definition (C++ Source):
    
    // standard C++ header:
    #include <string>
    
    // OS header:
    #ifdef _MSC_VER // Is this MSVC?
    #include <windows.h>
    #else // (not) _MSC_VER // Then it's hopefully Linux/g++.
    #include <unistd.h>
    #endif // _MSC_VER
    
    std::filesystem::path getExecPath()
    {
    #ifdef _MSC_VER // Is this MSVC?
      std::wstring path(1024, L'\0');
      const DWORD len
        = GetModuleFileNameW(NULL, &path[0], (DWORD)path.size());
      if (!len) return std::filesystem::path(); // ERROR!
      path.resize(len);
      return std::filesystem::path(path);
    #else // (not) _MSC_VER // Then it's hopefully Linux/g++.
      std::string path(1024, '\0');
      ssize_t len
        = readlink("/proc/self/exe", &path[0], path.size());
      if (len < 0) return std::filesystem::path(); // ERROR!
      path.resize(len);
      return path;
    #endif // _MSC_VER
    }
    
    /**************************************************************************/
    
    // Test:
    
    // standard C++ header:
    #include <iostream>
    
    int main()
    {
      std::cout
        << "Exec. Path:   " << getExecPath() << '\n'
        << "Current Dir.: " << std::filesystem::current_path() << '\n';
    }
    

    首先,我尝试了coliru

    g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && mkdir test ; cd test ; ../a.out
    Exec. Path:   "/tmp/1609670485.1182494/a.out"
    Current Dir.: "/tmp/1609670485.1182494/test"
    

    注意:coliru 上g++ 的版本是g++ 10.2.0(在撰写本文时)。


    我添加了一个CMakeLists.txt 来在我的本地盒子上进行测试:

    project(ExecPath)
    
    cmake_minimum_required(VERSION 3.10.0)
    
    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    set(CMAKE_CXX_EXTENSIONS OFF)
    if (UNIX)
      # std::filesystem which was added in C++17
      # seems to need an extra lib. in g++.
      # This might be version dependent...
      link_libraries(-lstdc++fs)
    endif()
    
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
    
    add_executable(testExecPath testExecPath.cc)
    

    Visual Studion 2019 中构建和运行的 MCVE。

    输出:

    Exec. Path:   "D:\\ds32737\\Entwicklung\\tests\\C++\\execPath\\build-VS2019\\bin\\Debug\\testExecPath.exe"
    Current Dir.: "D:\\ds32737\\Entwicklung\\tests\\C++\\execPath\\build-VS2019"
    

    最后,我在 Debian-Linux 上进行了测试(在具有 g++ 8.3.0 的 VM 中)。

    测试会话:

    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath$ mkdir build-debian
    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath$ cd build-debian/
    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ cmake ..
    -- The C compiler identification is GNU 8.3.0
    -- The CXX compiler identification is GNU 8.3.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /mnt/hostd/Entwicklung/tests/C++/execPath/build-debian
    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ cmake --build .
    Scanning dependencies of target testExecPath
    [ 50%] Building CXX object CMakeFiles/testExecPath.dir/testExecPath.cc.o
    [100%] Linking CXX executable bin/testExecPath
    [100%] Built target testExecPath
    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ bin/testExecPath 
    Exec. Path:   "/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian/bin/testExecPath"
    Current Dir.: "/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian"
    ds32737@debian:/mnt/hostd/Entwicklung/tests/C++/execPath/build-debian$ 
    

    g++ 8.3 是我最近安装的 Debian 的默认设置。有点老了。

    所以,我必须添加 -lstdc++fs 来解决与 std::filesystem 的链接问题。 请注意,我在 coliru 中使用的 g++ 10.2 不需要这样做。


    请注意,在每种情况下,我都注意当前工作目录不是可执行文件所在的目录。

    为了解决这个任务,我关注了编码问题。 (过去,当文件路径中出现除 ASCII 字符之外的所有字符时,我经常会遇到文件系统和编码问题。)

    因此,我在 Windows 实现中使用了GetModuleHandleW(),它以 UTF-16 格式返回文件路径。

    在 Linux 上,我假设始终使用 UTF-8。

    严格来说,std::filesystem::path 甚至没有必要。 相反,可以返回路径,例如作为std::string。 为此,Windows impl。可以将 UTF-16 转换为 UTF-8,以在任何平台上提供授权的编码。

    【讨论】:

    • 为代码 sn-p 提供适当的行距,这不是一个坏主意!
    猜你喜欢
    • 1970-01-01
    • 2012-07-21
    • 2018-11-02
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多