问题引入
项目中需要集成其他团队做的一个带有界面的工具。使用该工具时碰到了如下问题:
- 直接双击这个工具的exe,可以正常启动。
- 通过程序调用,或者用Everything打开这个工具,工具可以启动,但是界面上的图标全部消失了。
问题分析
打开命令行,执行目录切换到该exe目录下,然后调用该exe,正常启动。将执行目录切换到上上级目录,然后调用该exe,工具不能正常启动,图标全部消失。
通过分析,猜测该工具设置图标等资源文件时使用的是相对路径。
验证
生成一个程序read_a.exe,该程序用来打开该exe目录下面的a.txt文件(使用相对路径"./a.txt"),正常打开a.txt输出“Ok”,打开失败输出“Failed”。
#include <iostream>
#include <fstream>
int main() {
std::fstream file("./a.txt");
if ( !file.is_open() ) {
std::cout << "Failed!" << std::endl;
} else {
std::cout << "Ok!" << std::endl;
}
std::cin.get();
return 0;
}
将a.txt放到read_a.exe的目录下面。通过命令行切换到该目录,执行该程序read_a.exe。程序可以正常运行,成功打开a.txt。
将执行目录切换到上一级,然后执行该exe。程序打开a.txt失败。
通过程序调用。
#include <iostream>
int main() {
system("\"C:\\Users\\Admin\\Desktop\\CPP_TEST\\Debug\\read_a.exe\"");
return 0;
}
解决办法 1: 调用方切换工作路径
在调用时,将程序的工作目录临时切换到被调用程序read_a.exe的目录。调用之后再将工作目录切换到原来的路径。在Windows平台下面可以使用APISetCurrentDirectory和GetCurrentDirectory。
#include <windows.h>
#include <iostream>
int main() {
wchar_t backup_dir[MAX_PATH];
GetCurrentDirectory(MAX_PATH, backup_dir);
std::wcout << L"Backup dir: " << backup_dir << std::endl;
if ( SetCurrentDirectory(L"C:\\Users\\Admin\\Desktop\\CPP_TEST\\Debug\\") ) {
system("read_a.exe");
SetCurrentDirectory(backup_dir);
}
return 0;
}
解决方法 2:获取自身的执行路径
为了使用相对路径,更方便地加载文件,可以首先获得exe自身的绝对路径,然后用其拼接相对路径。
#include <windows.h>
#include <iostream>
#include <fstream>
int main() {
// 获取当前exe的路径。
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileName(NULL, path, MAX_PATH);
wchar_t drive[_MAX_DRIVE] = { 0 };
wchar_t dir[_MAX_DIR] = { 0 };
wchar_t file_name[_MAX_FNAME] = { 0 };
wchar_t ext[_MAX_EXT] = { 0 };
_wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, file_name, _MAX_FNAME, ext, _MAX_EXT);
// 拼接资源文件的相对路径。
std::wstring file_path = dir;
file_path += L"resource/a.txt";
std::fstream file(file_path);
if (!file.is_open()) {
std::cout << "Failed!" << std::endl;
} else {
std::cout << "Ok!" << std::endl;
}
std::cin.get();
return 0;
}