UP 优化调用实现 简单颜色匹配优化 加入主题切换
parent
0ab3543aa4
commit
d2113117cc
|
@ -127,10 +127,10 @@ private:
|
||||||
mutable std::mutex encoding_mutex_;
|
mutable std::mutex encoding_mutex_;
|
||||||
OutputEncoding output_encoding_;
|
OutputEncoding output_encoding_;
|
||||||
|
|
||||||
// 新增:工作目录相关
|
// 工作目录相关
|
||||||
mutable std::mutex working_dir_mutex_;
|
mutable std::mutex working_dir_mutex_;
|
||||||
std::string working_directory_;
|
std::string working_directory_;
|
||||||
bool use_auto_working_dir_;
|
bool use_auto_working_dir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLIPROCESS_H
|
#endif // CLIPROCESS_H
|
||||||
|
|
|
@ -7,6 +7,7 @@ AppState::AppState() :
|
||||||
show_main_window(true),
|
show_main_window(true),
|
||||||
auto_start(false),
|
auto_start(false),
|
||||||
auto_scroll_logs(true),
|
auto_scroll_logs(true),
|
||||||
|
enable_colored_logs(true),
|
||||||
max_log_lines(1000),
|
max_log_lines(1000),
|
||||||
stop_timeout_ms(5000),
|
stop_timeout_ms(5000),
|
||||||
use_stop_command(false),
|
use_stop_command(false),
|
||||||
|
@ -285,4 +286,4 @@ void AppState::ApplySettings() {
|
||||||
|
|
||||||
// 新增:应用输出编码设置
|
// 新增:应用输出编码设置
|
||||||
cli_process.SetOutputEncoding(output_encoding);
|
cli_process.SetOutputEncoding(output_encoding);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ CLIProcess::CLIProcess() {
|
||||||
max_log_lines_ = 1000;
|
max_log_lines_ = 1000;
|
||||||
stop_timeout_ms_ = 5000;
|
stop_timeout_ms_ = 5000;
|
||||||
output_encoding_ = OutputEncoding::AUTO_DETECT;
|
output_encoding_ = OutputEncoding::AUTO_DETECT;
|
||||||
use_auto_working_dir_ = true; // 新增:默认启用自动工作目录
|
use_auto_working_dir_ = false; // 自动工作目录
|
||||||
}
|
}
|
||||||
|
|
||||||
CLIProcess::~CLIProcess() {
|
CLIProcess::~CLIProcess() {
|
||||||
|
@ -383,17 +383,17 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
// 确定工作目录
|
// 确定工作目录
|
||||||
std::string working_dir;
|
std::wstring working_dir;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(working_dir_mutex_);
|
std::lock_guard<std::mutex> lock(working_dir_mutex_);
|
||||||
if (use_auto_working_dir_) {
|
if (use_auto_working_dir_) {
|
||||||
working_dir = ExtractDirectoryFromCommand(command);
|
working_dir = StringToWide(ExtractDirectoryFromCommand(command));
|
||||||
if (working_dir.empty()) {
|
if (working_dir.empty()) {
|
||||||
working_dir = std::filesystem::current_path().string();
|
working_dir = StringToWide(std::filesystem::current_path().string());
|
||||||
}
|
}
|
||||||
// AddLog("自动检测工作目录: " + working_dir);
|
// AddLog("自动检测工作目录: " + working_dir);
|
||||||
} else {
|
} else {
|
||||||
working_dir = working_directory_;
|
working_dir = StringToWide(working_directory_);
|
||||||
if (!working_dir.empty()) {
|
if (!working_dir.empty()) {
|
||||||
// AddLog("使用指定工作目录: " + working_dir);
|
// AddLog("使用指定工作目录: " + working_dir);
|
||||||
}
|
}
|
||||||
|
@ -401,192 +401,141 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SECURITY_ATTRIBUTES saAttr;
|
SECURITY_ATTRIBUTES sa;
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
saAttr.bInheritHandle = TRUE;
|
sa.bInheritHandle = TRUE;
|
||||||
saAttr.lpSecurityDescriptor = nullptr;
|
sa.lpSecurityDescriptor = nullptr;
|
||||||
|
|
||||||
HANDLE hReadTmp = nullptr;
|
if (!CreatePipe(&hReadPipe_, &hWritePipe_, &sa, 0)) {
|
||||||
HANDLE hWriteTmp = nullptr;
|
AddLog("创建输出管道失败");
|
||||||
|
|
||||||
if (!CreatePipe(&hReadTmp, &hWriteTmp, &saAttr, 0)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SetHandleInformation(hReadTmp, HANDLE_FLAG_INHERIT, 0)) {
|
|
||||||
CloseHandle(hReadTmp);
|
|
||||||
CloseHandle(hWriteTmp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE hReadTmp_stdin = nullptr;
|
if (!CreatePipe(&hReadPipe_stdin_, &hWritePipe_stdin_, &sa, 0)) {
|
||||||
HANDLE hWriteTmp_stdin = nullptr;
|
AddLog("创建输入管道失败");
|
||||||
if (!CreatePipe(&hReadTmp_stdin, &hWriteTmp_stdin, &saAttr, 0)) {
|
CloseHandle(hReadPipe_);
|
||||||
CloseHandle(hReadTmp);
|
CloseHandle(hWritePipe_);
|
||||||
CloseHandle(hWriteTmp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SetHandleInformation(hWriteTmp_stdin, HANDLE_FLAG_INHERIT, 0)) {
|
|
||||||
CloseHandle(hReadTmp);
|
|
||||||
CloseHandle(hWriteTmp);
|
|
||||||
CloseHandle(hReadTmp_stdin);
|
|
||||||
CloseHandle(hWriteTmp_stdin);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
STARTUPINFOA siStartInfo;
|
STARTUPINFO si;
|
||||||
ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA));
|
ZeroMemory(&si, sizeof(si));
|
||||||
siStartInfo.cb = sizeof(STARTUPINFOA);
|
si.cb = sizeof(si);
|
||||||
siStartInfo.hStdError = hWriteTmp;
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
siStartInfo.hStdOutput = hWriteTmp;
|
si.hStdOutput = hWritePipe_;
|
||||||
siStartInfo.hStdInput = hReadTmp_stdin;
|
si.hStdError = hWritePipe_;
|
||||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
|
si.hStdInput = hReadPipe_stdin_;
|
||||||
|
si.wShowWindow = SW_HIDE;
|
||||||
|
ZeroMemory(&pi_, sizeof(pi_));
|
||||||
|
|
||||||
PROCESS_INFORMATION piProcInfo;
|
// 转换命令为宽字符
|
||||||
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
|
std::wstring wcmd = StringToWide(command);
|
||||||
|
|
||||||
// Prepare environment block
|
// CreateProcess需要可修改的字符串
|
||||||
std::string env_block;
|
std::vector<wchar_t> cmdBuffer(wcmd.begin(), wcmd.end());
|
||||||
{
|
cmdBuffer.push_back(L'\0');
|
||||||
std::lock_guard<std::mutex> lock(env_mutex_);
|
|
||||||
for (const auto& kv : environment_variables_) {
|
|
||||||
env_block += kv.first + "=" + kv.second + '\0';
|
|
||||||
}
|
|
||||||
env_block += '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理工作目录 - 支持Unicode路径
|
// 使用Windows API设置环境变量
|
||||||
const char* working_dir_ptr = nullptr;
|
std::vector<std::pair<std::string, std::string>> originalEnvVars;
|
||||||
std::wstring working_dir_wide;
|
bool envVarsSet = false;
|
||||||
|
|
||||||
if (!working_dir.empty()) {
|
if (!environment_variables_.empty()) {
|
||||||
// 验证工作目录是否存在
|
envVarsSet = true;
|
||||||
if (!DirectoryExists(working_dir)) {
|
|
||||||
// AddLog("警告: 工作目录不存在,使用当前目录: " + working_dir);
|
|
||||||
working_dir = std::filesystem::current_path().string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为绝对路径
|
for (const auto &pair: environment_variables_) {
|
||||||
working_dir = GetAbsolutePath(working_dir);
|
if (!pair.first.empty()) {
|
||||||
working_dir_ptr = working_dir.c_str();
|
// 保存原始值(如果存在)
|
||||||
|
DWORD bufferSize = GetEnvironmentVariableA(pair.first.c_str(), nullptr, 0);
|
||||||
|
if (bufferSize > 0) {
|
||||||
|
// 变量存在,保存原始值
|
||||||
|
std::vector<char> buffer(bufferSize);
|
||||||
|
if (GetEnvironmentVariableA(pair.first.c_str(), buffer.data(), bufferSize) > 0) {
|
||||||
|
originalEnvVars.emplace_back(pair.first, std::string(buffer.data()));
|
||||||
|
} else {
|
||||||
|
originalEnvVars.emplace_back(pair.first, "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 变量不存在,标记为新变量(使用空字符串表示原来不存在)
|
||||||
|
originalEnvVars.emplace_back(pair.first, "");
|
||||||
|
}
|
||||||
|
|
||||||
// 如果路径包含非ASCII字符,需要使用CreateProcessW
|
// 设置新的环境变量值
|
||||||
bool hasNonAscii = false;
|
if (SetEnvironmentVariableA(pair.first.c_str(), pair.second.c_str())) {
|
||||||
for (char c : working_dir) {
|
// AddLog("设置环境变量: " + pair.first + "=" + pair.second);
|
||||||
if (static_cast<unsigned char>(c) > 127) {
|
} else {
|
||||||
hasNonAscii = true;
|
AddLog("设置环境变量失败: " + pair.first + " (错误代码: " + std::to_string(GetLastError()) + ")");
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNonAscii) {
|
|
||||||
// 转换为宽字符用于CreateProcessW
|
|
||||||
int wideSize = MultiByteToWideChar(CP_UTF8, 0, working_dir.c_str(), -1, nullptr, 0);
|
|
||||||
if (wideSize > 0) {
|
|
||||||
working_dir_wide.resize(wideSize);
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, working_dir.c_str(), -1, &working_dir_wide[0], wideSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL bSuccess = FALSE;
|
|
||||||
|
|
||||||
// 如果工作目录包含Unicode字符,使用CreateProcessW
|
|
||||||
if (!working_dir_wide.empty()) {
|
|
||||||
// 转换命令为宽字符
|
|
||||||
int cmdWideSize = MultiByteToWideChar(CP_UTF8, 0, command.c_str(), -1, nullptr, 0);
|
|
||||||
if (cmdWideSize > 0) {
|
|
||||||
std::wstring command_wide(cmdWideSize, L'\0');
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, command.c_str(), -1, &command_wide[0], cmdWideSize);
|
|
||||||
|
|
||||||
// 转换环境变量为宽字符
|
|
||||||
std::wstring env_block_wide;
|
|
||||||
if (!env_block.empty()) {
|
|
||||||
int envWideSize = MultiByteToWideChar(CP_UTF8, 0, env_block.c_str(), static_cast<int>(env_block.size()), nullptr, 0);
|
|
||||||
if (envWideSize > 0) {
|
|
||||||
env_block_wide.resize(envWideSize);
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, env_block.c_str(), static_cast<int>(env_block.size()), &env_block_wide[0], envWideSize);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STARTUPINFOW siStartInfoW;
|
|
||||||
ZeroMemory(&siStartInfoW, sizeof(STARTUPINFOW));
|
|
||||||
siStartInfoW.cb = sizeof(STARTUPINFOW);
|
|
||||||
siStartInfoW.hStdError = hWriteTmp;
|
|
||||||
siStartInfoW.hStdOutput = hWriteTmp;
|
|
||||||
siStartInfoW.hStdInput = hReadTmp_stdin;
|
|
||||||
siStartInfoW.dwFlags |= STARTF_USESTDHANDLES;
|
|
||||||
|
|
||||||
bSuccess = CreateProcessW(
|
|
||||||
nullptr,
|
|
||||||
&command_wide[0],
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
TRUE,
|
|
||||||
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
|
|
||||||
env_block_wide.empty() ? nullptr : (LPVOID)env_block_wide.data(),
|
|
||||||
working_dir_wide.c_str(),
|
|
||||||
&siStartInfoW,
|
|
||||||
&piProcInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddLog("环境变量设置完成,数量: " + std::to_string(environment_variables_.size()));
|
||||||
} else {
|
} else {
|
||||||
// 使用ANSI版本
|
AddLog("未设置自定义环境变量,使用默认环境 PWD:" + WideToString(working_dir));
|
||||||
bSuccess = CreateProcessA(
|
|
||||||
nullptr,
|
|
||||||
const_cast<char*>(command.c_str()),
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
TRUE,
|
|
||||||
CREATE_NO_WINDOW,
|
|
||||||
env_block.empty() ? nullptr : static_cast<LPVOID>(env_block.data()),
|
|
||||||
working_dir_ptr,
|
|
||||||
&siStartInfo,
|
|
||||||
&piProcInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(hWriteTmp);
|
BOOL result = CreateProcess(
|
||||||
CloseHandle(hReadTmp_stdin);
|
nullptr, // lpApplicationName
|
||||||
|
cmdBuffer.data(), // lpCommandLine
|
||||||
|
nullptr, // lpProcessAttributes
|
||||||
|
nullptr, // lpThreadAttributes
|
||||||
|
TRUE, // bInheritHandles
|
||||||
|
CREATE_NO_WINDOW, // dwCreationFlags
|
||||||
|
nullptr, // lpEnvironment (使用nullptr让子进程继承当前环境)
|
||||||
|
working_dir.empty() ? nullptr : working_dir.data(), // lpCurrentDirectory
|
||||||
|
&si, // lpStartupInfo
|
||||||
|
&pi_ // lpProcessInformation
|
||||||
|
);
|
||||||
|
|
||||||
if (!bSuccess) {
|
// 恢复原始环境变量
|
||||||
DWORD error = GetLastError();
|
if (envVarsSet) {
|
||||||
CloseHandle(hReadTmp);
|
for (const auto &pair: originalEnvVars) {
|
||||||
CloseHandle(hWriteTmp_stdin);
|
if (pair.second.empty()) {
|
||||||
AddLog("启动进程失败,错误代码: " + std::to_string(error));
|
// 原来不存在,删除变量
|
||||||
|
SetEnvironmentVariableA(pair.first.c_str(), nullptr);
|
||||||
// 提供更详细的错误信息
|
} else {
|
||||||
switch (error) {
|
// 恢复原始值
|
||||||
case ERROR_FILE_NOT_FOUND:
|
SetEnvironmentVariableA(pair.first.c_str(), pair.second.c_str());
|
||||||
AddLog("错误: 找不到指定的文件或程序");
|
}
|
||||||
break;
|
|
||||||
case ERROR_PATH_NOT_FOUND:
|
|
||||||
AddLog("错误: 找不到指定的路径");
|
|
||||||
break;
|
|
||||||
case ERROR_ACCESS_DENIED:
|
|
||||||
AddLog("错误: 访问被拒绝,可能需要管理员权限");
|
|
||||||
break;
|
|
||||||
case ERROR_INVALID_PARAMETER:
|
|
||||||
AddLog("错误: 无效的参数");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
AddLog("错误: 未知错误,请检查命令和路径是否正确");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseProcessHandles();
|
if (result) {
|
||||||
|
AddLog("进程已启动: " + command + " PID: " + std::to_string(pi_.dwProcessId));
|
||||||
|
|
||||||
pi_ = piProcInfo;
|
CloseHandle(hWritePipe_);
|
||||||
hReadPipe_ = hReadTmp;
|
CloseHandle(hReadPipe_stdin_);
|
||||||
hWritePipe_stdin_ = hWriteTmp_stdin;
|
hWritePipe_ = nullptr;
|
||||||
|
hReadPipe_stdin_ = nullptr;
|
||||||
|
|
||||||
AddLog("进程已启动,PID: " + std::to_string(piProcInfo.dwProcessId));
|
output_thread_ = std::thread([this]() {
|
||||||
if (!working_dir.empty()) {
|
ReadOutput();
|
||||||
AddLog("工作目录: " + working_dir);
|
});
|
||||||
|
} else {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
|
||||||
|
// 获取详细的错误信息
|
||||||
|
LPWSTR messageBuffer = nullptr;
|
||||||
|
size_t size = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR) &messageBuffer, 0, nullptr);
|
||||||
|
|
||||||
|
std::string errorMsg = "CreateProcess 失败 (错误代码: " + std::to_string(err) + ")";
|
||||||
|
if (messageBuffer) {
|
||||||
|
std::wstring wErrorMsg(messageBuffer);
|
||||||
|
errorMsg += " - " + WideToString(wErrorMsg);
|
||||||
|
LocalFree(messageBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddLog(errorMsg);
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
|
CloseHandle(hReadPipe_);
|
||||||
|
CloseHandle(hWritePipe_);
|
||||||
|
CloseHandle(hReadPipe_stdin_);
|
||||||
|
CloseHandle(hWritePipe_stdin_);
|
||||||
|
hReadPipe_ = hWritePipe_ = hReadPipe_stdin_ = hWritePipe_stdin_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start output reading thread
|
|
||||||
output_thread_ = std::thread(&CLIProcess::ReadOutput, this);
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Unix/Linux implementation
|
// Unix/Linux implementation
|
||||||
int pipe_out[2];
|
int pipe_out[2];
|
||||||
|
|
|
@ -272,7 +272,12 @@ void Manager::RenderMenuBar() {
|
||||||
RenderLayoutMenu();
|
RenderLayoutMenu();
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginMenu("主题")) {
|
||||||
|
if (ImGui::MenuItem("暗黑(Dark)")) { ImGui::StyleColorsDark(); }
|
||||||
|
if (ImGui::MenuItem("明亮(Light)")) { ImGui::StyleColorsLight(); }
|
||||||
|
if (ImGui::MenuItem("经典(Classic)")) { ImGui::StyleColorsClassic(); }
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
ImGui::EndMenuBar();
|
ImGui::EndMenuBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -994,11 +999,14 @@ void Manager::RenderLogPanel() {
|
||||||
|
|
||||||
ImVec4 Manager::GetLogLevelColor(const std::string& log) {
|
ImVec4 Manager::GetLogLevelColor(const std::string& log) {
|
||||||
// 简单的日志级别颜色区分
|
// 简单的日志级别颜色区分
|
||||||
if (log.find("[ERROR]") != std::string::npos || log.find("error") != std::string::npos) {
|
if (log.find("错误") != std::string::npos || log.find("[E]") != std::string::npos ||
|
||||||
|
log.find("[ERROR]") != std::string::npos || log.find("error") != std::string::npos) {
|
||||||
return ImVec4(1.0f, 0.4f, 0.4f, 1.0f); // 红色
|
return ImVec4(1.0f, 0.4f, 0.4f, 1.0f); // 红色
|
||||||
} else if (log.find("[WARN]") != std::string::npos || log.find("warning") != std::string::npos) {
|
} else if (log.find("警告") != std::string::npos || log.find("[W]") != std::string::npos ||
|
||||||
|
log.find("[WARN]") != std::string::npos || log.find("warning") != std::string::npos) {
|
||||||
return ImVec4(1.0f, 1.0f, 0.4f, 1.0f); // 黄色
|
return ImVec4(1.0f, 1.0f, 0.4f, 1.0f); // 黄色
|
||||||
} else if (log.find("[INFO]") != std::string::npos || log.find("info") != std::string::npos) {
|
} else if (log.find("信息") != std::string::npos || log.find("[I]") != std::string::npos ||
|
||||||
|
log.find("[INFO]") != std::string::npos || log.find("info") != std::string::npos) {
|
||||||
return ImVec4(0.4f, 1.0f, 0.4f, 1.0f); // 绿色
|
return ImVec4(0.4f, 1.0f, 0.4f, 1.0f); // 绿色
|
||||||
}
|
}
|
||||||
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色
|
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色
|
||||||
|
@ -1793,4 +1801,4 @@ extern "C" {
|
||||||
void* GetMacAppDelegate();
|
void* GetMacAppDelegate();
|
||||||
void* GetMacTrayIcon();
|
void* GetMacTrayIcon();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue