获取可执行文件路径

新手上路,请多包涵

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

我要做的就是以独立于平台的方式获取当前运行的可执行文件的路径,可以是绝对路径,也可以是相对于调用可执行文件的位置。我虽然 boost::filesystem::initial_path 是我的麻烦的答案,但这似乎只处理问题的“平台无关”部分——它仍然返回调用应用程序的路径。

对于一些背景知识,这是一个使用 Ogre 的游戏,我正在尝试使用 Very Sleepy 对其进行分析,它从自己的目录运行目标可执行文件,所以当然在加载时游戏找不到配置文件等并立即崩溃.我希望能够将其传递给配置文件的绝对路径,我知道该配置文件将始终与可执行文件一起存在。在 Visual Studio 中进行调试也是如此——我希望能够运行 $(TargetPath) 而无需设置工作目录。

原文由 Ben Hymers 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

有几个答案建议在 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
}

原文由 PolarBear 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题