UP 彩色日志 动态界面 立即回收
parent
7b6c87f2b0
commit
0ab3543aa4
|
@ -28,6 +28,7 @@ public:
|
||||||
char command_input[256]{};
|
char command_input[256]{};
|
||||||
char send_command[256]{};
|
char send_command[256]{};
|
||||||
bool auto_scroll_logs;
|
bool auto_scroll_logs;
|
||||||
|
bool enable_colored_logs;
|
||||||
int max_log_lines;
|
int max_log_lines;
|
||||||
char web_url[256]{};
|
char web_url[256]{};
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
void SetEnvironmentVariables(const std::map<std::string, std::string>& env_vars);
|
void SetEnvironmentVariables(const std::map<std::string, std::string>& env_vars);
|
||||||
void SetOutputEncoding(OutputEncoding encoding);
|
void SetOutputEncoding(OutputEncoding encoding);
|
||||||
|
|
||||||
|
// 新增:工作目录设置
|
||||||
|
void SetWorkingDirectory(const std::string& working_dir);
|
||||||
|
std::string GetWorkingDirectory() const;
|
||||||
|
|
||||||
void Start(const std::string& command);
|
void Start(const std::string& command);
|
||||||
void Stop();
|
void Stop();
|
||||||
void Restart(const std::string& command);
|
void Restart(const std::string& command);
|
||||||
|
@ -69,6 +73,11 @@ public:
|
||||||
static std::string GetEncodingName(OutputEncoding encoding);
|
static std::string GetEncodingName(OutputEncoding encoding);
|
||||||
static std::vector<std::pair<OutputEncoding, std::string>> GetSupportedEncodings();
|
static std::vector<std::pair<OutputEncoding, std::string>> GetSupportedEncodings();
|
||||||
|
|
||||||
|
// 新增:工作目录相关静态方法
|
||||||
|
static std::string ExtractDirectoryFromCommand(const std::string& command);
|
||||||
|
static std::string GetAbsolutePath(const std::string& path);
|
||||||
|
static bool DirectoryExists(const std::string& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ReadOutput();
|
void ReadOutput();
|
||||||
void CloseProcessHandles();
|
void CloseProcessHandles();
|
||||||
|
@ -117,6 +126,11 @@ private:
|
||||||
// 编码相关
|
// 编码相关
|
||||||
mutable std::mutex encoding_mutex_;
|
mutable std::mutex encoding_mutex_;
|
||||||
OutputEncoding output_encoding_;
|
OutputEncoding output_encoding_;
|
||||||
|
|
||||||
|
// 新增:工作目录相关
|
||||||
|
mutable std::mutex working_dir_mutex_;
|
||||||
|
std::string working_directory_;
|
||||||
|
bool use_auto_working_dir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLIPROCESS_H
|
#endif // CLIPROCESS_H
|
|
@ -1,111 +1,171 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// 系统头文件
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// 第三方库头文件
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
|
// 平台相关头文件
|
||||||
|
#ifdef USE_WIN32_BACKEND
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "imgui_impl_win32.h"
|
||||||
|
#include "imgui_impl_dx11.h"
|
||||||
|
#else
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <GLFW/glfw3native.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#elif __APPLE__
|
||||||
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <GLFW/glfw3native.h>
|
||||||
|
#else
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#endif
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 项目头文件
|
||||||
#include "AppState.h"
|
#include "AppState.h"
|
||||||
#include "TrayIcon.h"
|
#include "TrayIcon.h"
|
||||||
|
|
||||||
#ifdef USE_WIN32_BACKEND
|
|
||||||
#include <d3d11.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include "imgui_impl_win32.h"
|
|
||||||
#include "imgui_impl_dx11.h"
|
|
||||||
#else
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <GLFW/glfw3native.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#elif __APPLE__
|
|
||||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <GLFW/glfw3native.h>
|
|
||||||
#else
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#endif
|
|
||||||
#include "imgui_impl_glfw.h"
|
|
||||||
#include "imgui_impl_opengl3.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class Manager {
|
class Manager {
|
||||||
public:
|
public:
|
||||||
|
// 构造函数和析构函数
|
||||||
Manager();
|
Manager();
|
||||||
~Manager();
|
~Manager();
|
||||||
|
|
||||||
bool Initialize();
|
// 核心生命周期管理
|
||||||
void Run();
|
bool Initialize(); // 初始化应用程序
|
||||||
void Shutdown();
|
void Run(); // 运行主循环
|
||||||
|
void Shutdown(); // 关闭应用程序
|
||||||
|
|
||||||
void OnTrayShowWindow();
|
// 托盘事件回调
|
||||||
void OnTrayExit();
|
void OnTrayShowWindow(); // 托盘显示窗口事件
|
||||||
|
void OnTrayExit(); // 托盘退出事件
|
||||||
AppState m_app_state;
|
|
||||||
|
|
||||||
|
// 公共成员变量
|
||||||
|
AppState m_app_state; // 应用程序状态
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// UI渲染
|
// 枚举类型定义
|
||||||
void RenderUI();
|
enum class LayoutPreset {
|
||||||
void RenderMenuBar();
|
Classic, // 经典布局
|
||||||
void RenderMainContent();
|
Development, // 开发布局
|
||||||
void RenderSettingsMenu();
|
Monitoring // 监控布局
|
||||||
void RenderStopCommandSettings();
|
};
|
||||||
void RenderEnvironmentVariablesSettings();
|
|
||||||
void RenderOutputEncodingSettings();
|
|
||||||
|
|
||||||
// 事件处理
|
// 结构体定义
|
||||||
void HandleMessages();
|
struct ColoredTextSegment {
|
||||||
bool ShouldExit() const;
|
std::string text; // 文本内容
|
||||||
void ShowMainWindow();
|
ImVec4 color; // 文本颜色
|
||||||
void HideMainWindow();
|
};
|
||||||
|
|
||||||
// 平台相关初始化
|
// UI渲染相关方法
|
||||||
|
void RenderUI(); // 渲染主UI
|
||||||
|
void RenderMenuBar(); // 渲染菜单栏
|
||||||
|
void RenderMainContent(); // 渲染主内容区域
|
||||||
|
void RenderSettingsMenu(); // 渲染设置菜单
|
||||||
|
void RenderStopCommandSettings(); // 渲染停止命令设置
|
||||||
|
void RenderEnvironmentVariablesSettings(); // 渲染环境变量设置
|
||||||
|
void RenderOutputEncodingSettings(); // 渲染输出编码设置
|
||||||
|
void RenderControlPanel(float buttonWidth, float buttonHeight, float inputWidth); // 渲染控制面板
|
||||||
|
void RenderCommandPanel(float buttonWidth, float inputWidth); // 渲染命令面板
|
||||||
|
void RenderLogPanel(); // 渲染日志面板
|
||||||
|
void RenderCommandHistory(); // 渲染命令历史
|
||||||
|
void RenderStatusMessages(); // 渲染状态消息
|
||||||
|
|
||||||
|
// 布局管理相关方法
|
||||||
|
void SetupDefaultDockingLayout(ImGuiID dockspace_id); // 设置默认停靠布局
|
||||||
|
void RenderLayoutMenu(); // 渲染布局菜单
|
||||||
|
static void ApplyPresetLayout(LayoutPreset preset); // 应用预设布局
|
||||||
|
void SaveCurrentLayout(); // 保存当前布局
|
||||||
|
void LoadSavedLayout(); // 加载已保存布局
|
||||||
|
|
||||||
|
// 日志颜色处理方法
|
||||||
|
ImVec4 GetLogLevelColor(const std::string& log); // 获取日志级别颜色
|
||||||
|
void RenderColoredLogLine(const std::string& log); // 渲染彩色日志行
|
||||||
|
std::vector<ColoredTextSegment> ParseAnsiColorCodes(const std::string& text); // 解析ANSI颜色代码
|
||||||
|
std::pair<ImVec4, bool> ParseAnsiColorCode(const std::string& code, const ImVec4& currentColor, bool currentBold); // 解析单个ANSI颜色代码
|
||||||
|
ImVec4 GetAnsiColor(int colorIndex, bool bright); // 获取ANSI颜色
|
||||||
|
|
||||||
|
// 事件处理相关方法
|
||||||
|
void HandleMessages(); // 处理消息
|
||||||
|
bool ShouldExit() const; // 检查是否应该退出
|
||||||
|
static void ContentScaleCallback(GLFWwindow *window, float xscale, float yscale); // 内容缩放回调
|
||||||
|
|
||||||
|
// 窗口管理相关方法
|
||||||
|
void ShowMainWindow(); // 显示主窗口
|
||||||
|
void HideMainWindow(); // 隐藏主窗口
|
||||||
|
|
||||||
|
// DPI相关方法
|
||||||
|
void UpdateDPIScale(); // 更新DPI缩放
|
||||||
|
void ReloadFonts() const; // 重新加载字体
|
||||||
|
|
||||||
|
// 平台相关初始化方法
|
||||||
#ifdef USE_WIN32_BACKEND
|
#ifdef USE_WIN32_BACKEND
|
||||||
bool InitializeWin32();
|
bool InitializeWin32(); // 初始化Win32
|
||||||
bool InitializeDirectX11();
|
bool InitializeDirectX11(); // 初始化DirectX11
|
||||||
void CleanupWin32();
|
void CleanupWin32(); // 清理Win32
|
||||||
void CleanupDirectX11();
|
void CleanupDirectX11(); // 清理DirectX11
|
||||||
static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 窗口过程
|
||||||
|
|
||||||
HWND m_hwnd = nullptr;
|
|
||||||
WNDCLASSEX m_wc = {};
|
|
||||||
ID3D11Device* m_pd3dDevice = nullptr;
|
|
||||||
ID3D11DeviceContext* m_pd3dDeviceContext = nullptr;
|
|
||||||
IDXGISwapChain* m_pSwapChain = nullptr;
|
|
||||||
ID3D11RenderTargetView* m_mainRenderTargetView = nullptr;
|
|
||||||
#else
|
#else
|
||||||
bool InitializeGLFW();
|
bool InitializeGLFW(); // 初始化GLFW
|
||||||
void CleanupGLFW();
|
void CleanupGLFW(); // 清理GLFW
|
||||||
static void GlfwErrorCallback(int error, const char* description);
|
static void GlfwErrorCallback(int error, const char* description); // GLFW错误回调
|
||||||
|
|
||||||
GLFWwindow* m_window = nullptr;
|
|
||||||
const char* m_glsl_version = nullptr;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 托盘相关
|
// 托盘相关方法
|
||||||
bool InitializeTray();
|
bool InitializeTray(); // 初始化托盘
|
||||||
void CleanupTray();
|
void CleanupTray(); // 清理托盘
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static HWND CreateHiddenWindow();
|
static HWND CreateHiddenWindow(); // 创建隐藏窗口
|
||||||
HWND m_tray_hwnd = nullptr;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::unique_ptr<TrayIcon> m_tray;
|
// 平台相关成员变量
|
||||||
|
#ifdef USE_WIN32_BACKEND
|
||||||
|
HWND m_hwnd = nullptr; // 窗口句柄
|
||||||
|
WNDCLASSEX m_wc = {}; // 窗口类
|
||||||
|
ID3D11Device* m_pd3dDevice = nullptr; // D3D11设备
|
||||||
|
ID3D11DeviceContext* m_pd3dDeviceContext = nullptr; // D3D11设备上下文
|
||||||
|
IDXGISwapChain* m_pSwapChain = nullptr; // 交换链
|
||||||
|
ID3D11RenderTargetView* m_mainRenderTargetView = nullptr; // 主渲染目标视图
|
||||||
|
#else
|
||||||
|
GLFWwindow* m_window = nullptr; // GLFW窗口
|
||||||
|
const char* m_glsl_version = nullptr; // GLSL版本
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 托盘相关成员变量
|
||||||
|
std::unique_ptr<TrayIcon> m_tray; // 托盘图标
|
||||||
|
#ifdef _WIN32
|
||||||
|
HWND m_tray_hwnd = nullptr; // 托盘窗口句柄
|
||||||
|
#endif
|
||||||
|
|
||||||
// 控制标志
|
// 控制标志
|
||||||
bool m_should_exit = false;
|
bool m_should_exit = false; // 是否应该退出
|
||||||
bool m_initialized = false;
|
bool m_initialized = false; // 是否已初始化
|
||||||
|
|
||||||
// DPI缩放因子
|
// DPI缩放相关
|
||||||
float m_dpi_scale = 1.0f;
|
float m_dpi_scale = 1.0f; // 当前DPI缩放
|
||||||
|
float m_last_dpi_scale = 1.0f; // 上次DPI缩放
|
||||||
|
|
||||||
// 环境变量UI状态
|
// 布局相关成员变量
|
||||||
char env_key_input_[256] = {};
|
bool m_apply_preset_layout = false; // 是否需要应用预设布局
|
||||||
char env_value_input_[512] = {};
|
LayoutPreset m_pending_preset = LayoutPreset::Classic; // 待应用的预设布局
|
||||||
bool show_env_settings_ = false;
|
bool m_reset_layout = false; // 是否重置布局
|
||||||
|
bool m_show_save_success = false; // 是否显示保存成功消息
|
||||||
|
bool m_show_load_success = false; // 是否显示加载成功消息
|
||||||
|
float m_save_success_timer = 0.0f; // 保存成功消息计时器
|
||||||
|
float m_load_success_timer = 0.0f; // 加载成功消息计时器
|
||||||
|
|
||||||
// 编码设置UI状态
|
// UI状态相关成员变量
|
||||||
bool show_encoding_settings_ = false;
|
char env_key_input_[256] = {}; // 环境变量键输入缓冲区
|
||||||
// 历史命令UI状态
|
char env_value_input_[512] = {}; // 环境变量值输入缓冲区
|
||||||
bool show_command_history_;
|
bool show_env_settings_ = false; // 是否显示环境变量设置
|
||||||
|
bool show_encoding_settings_ = false; // 是否显示编码设置
|
||||||
|
bool show_command_history_ = false; // 是否显示命令历史
|
||||||
};
|
};
|
|
@ -191,6 +191,9 @@ void AppState::LoadSettings() {
|
||||||
else if (key == "AutoScrollLogs") {
|
else if (key == "AutoScrollLogs") {
|
||||||
auto_scroll_logs = (value == "1");
|
auto_scroll_logs = (value == "1");
|
||||||
}
|
}
|
||||||
|
else if (key == "EnableColoredLogs") {
|
||||||
|
enable_colored_logs = (value == "1");
|
||||||
|
}
|
||||||
else if (key == "AutoStart") {
|
else if (key == "AutoStart") {
|
||||||
auto_start = (value == "1");
|
auto_start = (value == "1");
|
||||||
}
|
}
|
||||||
|
@ -238,6 +241,7 @@ void AppState::SaveSettings() {
|
||||||
file << "CommandInput=" << command_input << "\n";
|
file << "CommandInput=" << command_input << "\n";
|
||||||
file << "MaxLogLines=" << max_log_lines << "\n";
|
file << "MaxLogLines=" << max_log_lines << "\n";
|
||||||
file << "AutoScrollLogs=" << (auto_scroll_logs ? "1" : "0") << "\n";
|
file << "AutoScrollLogs=" << (auto_scroll_logs ? "1" : "0") << "\n";
|
||||||
|
file << "EnableColoredLogs=" << (enable_colored_logs ? "1" : "0") << "\n";
|
||||||
file << "AutoStart=" << (auto_start ? "1" : "0") << "\n";
|
file << "AutoStart=" << (auto_start ? "1" : "0") << "\n";
|
||||||
file << "WebUrl=" << web_url << "\n";
|
file << "WebUrl=" << web_url << "\n";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "CLIProcess.h"
|
#include "CLIProcess.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
|
@ -20,17 +21,122 @@ CLIProcess::CLIProcess() {
|
||||||
process_pid_ = -1;
|
process_pid_ = -1;
|
||||||
pipe_stdout_[0] = pipe_stdout_[1] = -1;
|
pipe_stdout_[0] = pipe_stdout_[1] = -1;
|
||||||
pipe_stdin_[0] = pipe_stdin_[1] = -1;
|
pipe_stdin_[0] = pipe_stdin_[1] = -1;
|
||||||
process__running_ = false;
|
process_running_ = false;
|
||||||
#endif
|
#endif
|
||||||
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; // 新增:默认启用自动工作目录
|
||||||
}
|
}
|
||||||
|
|
||||||
CLIProcess::~CLIProcess() {
|
CLIProcess::~CLIProcess() {
|
||||||
Stop();
|
Stop();
|
||||||
CleanupResources();
|
CleanupResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:设置工作目录
|
||||||
|
void CLIProcess::SetWorkingDirectory(const std::string& working_dir) {
|
||||||
|
std::lock_guard<std::mutex> lock(working_dir_mutex_);
|
||||||
|
if (working_dir.empty()) {
|
||||||
|
use_auto_working_dir_ = true;
|
||||||
|
working_directory_.clear();
|
||||||
|
} else {
|
||||||
|
if (DirectoryExists(working_dir)) {
|
||||||
|
working_directory_ = GetAbsolutePath(working_dir);
|
||||||
|
use_auto_working_dir_ = false;
|
||||||
|
AddLog("工作目录已设置为: " + working_directory_);
|
||||||
|
} else {
|
||||||
|
AddLog("警告: 指定的工作目录不存在: " + working_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:获取当前工作目录设置
|
||||||
|
std::string CLIProcess::GetWorkingDirectory() const {
|
||||||
|
std::lock_guard<std::mutex> lock(working_dir_mutex_);
|
||||||
|
return working_directory_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:从命令中提取目录路径
|
||||||
|
std::string CLIProcess::ExtractDirectoryFromCommand(const std::string& command) {
|
||||||
|
if (command.empty()) return "";
|
||||||
|
|
||||||
|
std::string trimmed_command = command;
|
||||||
|
|
||||||
|
// 移除前后空格
|
||||||
|
size_t start = trimmed_command.find_first_not_of(" \t\r\n");
|
||||||
|
if (start == std::string::npos) return "";
|
||||||
|
|
||||||
|
size_t end = trimmed_command.find_last_not_of(" \t\r\n");
|
||||||
|
trimmed_command = trimmed_command.substr(start, end - start + 1);
|
||||||
|
|
||||||
|
std::string executable_path;
|
||||||
|
|
||||||
|
// 处理引号包围的路径
|
||||||
|
if (trimmed_command[0] == '"') {
|
||||||
|
size_t quote_end = trimmed_command.find('"', 1);
|
||||||
|
if (quote_end != std::string::npos) {
|
||||||
|
executable_path = trimmed_command.substr(1, quote_end - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 找到第一个空格前的部分作为可执行文件路径
|
||||||
|
size_t space_pos = trimmed_command.find(' ');
|
||||||
|
if (space_pos != std::string::npos) {
|
||||||
|
executable_path = trimmed_command.substr(0, space_pos);
|
||||||
|
} else {
|
||||||
|
executable_path = trimmed_command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executable_path.empty()) return "";
|
||||||
|
|
||||||
|
// 使用 std::filesystem 来处理路径
|
||||||
|
try {
|
||||||
|
std::filesystem::path path(executable_path);
|
||||||
|
|
||||||
|
// 如果是相对路径,转换为绝对路径
|
||||||
|
if (path.is_relative()) {
|
||||||
|
path = std::filesystem::absolute(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (std::filesystem::exists(path) && std::filesystem::is_regular_file(path)) {
|
||||||
|
return path.parent_path().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果文件不存在,但路径看起来像一个文件路径,返回其父目录
|
||||||
|
if (path.has_parent_path()) {
|
||||||
|
auto parent = path.parent_path();
|
||||||
|
if (std::filesystem::exists(parent) && std::filesystem::is_directory(parent)) {
|
||||||
|
return parent.string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
// 路径解析失败,返回当前工作目录
|
||||||
|
return std::filesystem::current_path().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:获取绝对路径
|
||||||
|
std::string CLIProcess::GetAbsolutePath(const std::string& path) {
|
||||||
|
try {
|
||||||
|
return std::filesystem::absolute(path).string();
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:检查目录是否存在
|
||||||
|
bool CLIProcess::DirectoryExists(const std::string& path) {
|
||||||
|
try {
|
||||||
|
return std::filesystem::exists(path) && std::filesystem::is_directory(path);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置输出编码
|
// 设置输出编码
|
||||||
void CLIProcess::SetOutputEncoding(OutputEncoding encoding) {
|
void CLIProcess::SetOutputEncoding(OutputEncoding encoding) {
|
||||||
std::lock_guard<std::mutex> lock(encoding_mutex_);
|
std::lock_guard<std::mutex> lock(encoding_mutex_);
|
||||||
|
@ -228,7 +334,7 @@ void CLIProcess::SetEnvironmentVariables(const std::map<std::string, std::string
|
||||||
if (!environment_variables_.empty()) {
|
if (!environment_variables_.empty()) {
|
||||||
// AddLog("已设置 " + std::to_string(environment_variables_.size()) + " 个有效环境变量");
|
// AddLog("已设置 " + std::to_string(environment_variables_.size()) + " 个有效环境变量");
|
||||||
for (const auto& pair : environment_variables_) {
|
for (const auto& pair : environment_variables_) {
|
||||||
AddLog(" " + pair.first + "=" + pair.second);
|
// AddLog(" " + pair.first + "=" + pair.second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// AddLog("已清空所有自定义环境变量");
|
// AddLog("已清空所有自定义环境变量");
|
||||||
|
@ -275,6 +381,25 @@ void CLIProcess::ClearEnvironmentVariables() {
|
||||||
|
|
||||||
void CLIProcess::Start(const std::string& command) {
|
void CLIProcess::Start(const std::string& command) {
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
|
// 确定工作目录
|
||||||
|
std::string working_dir;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(working_dir_mutex_);
|
||||||
|
if (use_auto_working_dir_) {
|
||||||
|
working_dir = ExtractDirectoryFromCommand(command);
|
||||||
|
if (working_dir.empty()) {
|
||||||
|
working_dir = std::filesystem::current_path().string();
|
||||||
|
}
|
||||||
|
// AddLog("自动检测工作目录: " + working_dir);
|
||||||
|
} else {
|
||||||
|
working_dir = working_directory_;
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
// AddLog("使用指定工作目录: " + working_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SECURITY_ATTRIBUTES saAttr;
|
SECURITY_ATTRIBUTES saAttr;
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
@ -329,24 +454,122 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
env_block += '\0';
|
env_block += '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL bSuccess = CreateProcessA(
|
// 处理工作目录 - 支持Unicode路径
|
||||||
nullptr,
|
const char* working_dir_ptr = nullptr;
|
||||||
const_cast<char*>(command.c_str()),
|
std::wstring working_dir_wide;
|
||||||
nullptr,
|
|
||||||
nullptr,
|
if (!working_dir.empty()) {
|
||||||
TRUE,
|
// 验证工作目录是否存在
|
||||||
CREATE_NO_WINDOW,
|
if (!DirectoryExists(working_dir)) {
|
||||||
env_block.empty() ? nullptr : (LPVOID)env_block.data(),
|
// AddLog("警告: 工作目录不存在,使用当前目录: " + working_dir);
|
||||||
nullptr,
|
working_dir = std::filesystem::current_path().string();
|
||||||
&siStartInfo,
|
}
|
||||||
&piProcInfo);
|
|
||||||
|
// 转换为绝对路径
|
||||||
|
working_dir = GetAbsolutePath(working_dir);
|
||||||
|
working_dir_ptr = working_dir.c_str();
|
||||||
|
|
||||||
|
// 如果路径包含非ASCII字符,需要使用CreateProcessW
|
||||||
|
bool hasNonAscii = false;
|
||||||
|
for (char c : working_dir) {
|
||||||
|
if (static_cast<unsigned char>(c) > 127) {
|
||||||
|
hasNonAscii = true;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 使用ANSI版本
|
||||||
|
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);
|
CloseHandle(hWriteTmp);
|
||||||
CloseHandle(hReadTmp_stdin);
|
CloseHandle(hReadTmp_stdin);
|
||||||
|
|
||||||
if (!bSuccess) {
|
if (!bSuccess) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
CloseHandle(hReadTmp);
|
CloseHandle(hReadTmp);
|
||||||
CloseHandle(hWriteTmp_stdin);
|
CloseHandle(hWriteTmp_stdin);
|
||||||
|
AddLog("启动进程失败,错误代码: " + std::to_string(error));
|
||||||
|
|
||||||
|
// 提供更详细的错误信息
|
||||||
|
switch (error) {
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,15 +579,21 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
hReadPipe_ = hReadTmp;
|
hReadPipe_ = hReadTmp;
|
||||||
hWritePipe_stdin_ = hWriteTmp_stdin;
|
hWritePipe_stdin_ = hWriteTmp_stdin;
|
||||||
|
|
||||||
|
AddLog("进程已启动,PID: " + std::to_string(piProcInfo.dwProcessId));
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
AddLog("工作目录: " + working_dir);
|
||||||
|
}
|
||||||
|
|
||||||
// Start output reading thread
|
// Start output reading thread
|
||||||
output_thread_ = std::thread(&CLIProcess::ReadOutput, this);
|
output_thread_ = std::thread(&CLIProcess::ReadOutput, this);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Unix/Linux implementation using posix_spawn
|
// Unix/Linux implementation
|
||||||
int pipe_out[2];
|
int pipe_out[2];
|
||||||
int pipe_in[2];
|
int pipe_in[2];
|
||||||
|
|
||||||
if (pipe(pipe_out) < 0 || pipe(pipe_in) < 0) {
|
if (pipe(pipe_out) < 0 || pipe(pipe_in) < 0) {
|
||||||
|
AddLog("创建管道失败: " + std::string(strerror(errno)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,10 +609,26 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
dup2(pipe_in[0], STDIN_FILENO);
|
dup2(pipe_in[0], STDIN_FILENO);
|
||||||
close(pipe_in[0]);
|
close(pipe_in[0]);
|
||||||
|
|
||||||
|
// 设置工作目录
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
if (!DirectoryExists(working_dir)) {
|
||||||
|
fprintf(stderr, "警告: 工作目录不存在: %s\n", working_dir.c_str());
|
||||||
|
working_dir = std::filesystem::current_path().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(working_dir.c_str()) != 0) {
|
||||||
|
fprintf(stderr, "无法切换到工作目录: %s, 错误: %s\n",
|
||||||
|
working_dir.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare environment variables
|
// Prepare environment variables
|
||||||
if (!environment_variables_.empty()) {
|
{
|
||||||
for (const auto& kv : environment_variables_) {
|
std::lock_guard<std::mutex> lock(env_mutex_);
|
||||||
setenv(kv.first.c_str(), kv.second.c_str(), 1);
|
if (!environment_variables_.empty()) {
|
||||||
|
for (const auto& kv : environment_variables_) {
|
||||||
|
setenv(kv.first.c_str(), kv.second.c_str(), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,8 +648,19 @@ void CLIProcess::Start(const std::string& command) {
|
||||||
|
|
||||||
process_running_ = true;
|
process_running_ = true;
|
||||||
|
|
||||||
|
AddLog("进程已启动,PID: " + std::to_string(pid));
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
AddLog("工作目录: " + working_dir);
|
||||||
|
}
|
||||||
|
|
||||||
// Start output reading thread
|
// Start output reading thread
|
||||||
output_thread_ = std::thread(&CLIProcess::ReadOutput, this);
|
output_thread_ = std::thread(&CLIProcess::ReadOutput, this);
|
||||||
|
} else {
|
||||||
|
AddLog("fork失败,无法启动进程: " + std::string(strerror(errno)));
|
||||||
|
close(pipe_out[0]);
|
||||||
|
close(pipe_out[1]);
|
||||||
|
close(pipe_in[0]);
|
||||||
|
close(pipe_in[1]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
1046
app/src/Manager.cpp
1046
app/src/Manager.cpp
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue