CLI_Manager/app/src/CLIProcess.cpp

902 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "CLIProcess.h"
#include <algorithm>
#include <cstdio>
#include <filesystem>
#ifdef _WIN32
#include "Units.h"
#else
#include <iconv.h>
#include <locale.h>
#include <langinfo.h>
#include <spawn.h>
extern char **environ;
#endif
CLIProcess::CLIProcess() {
#ifdef _WIN32
ZeroMemory(&pi_, sizeof(pi_));
hWritePipe_stdin_ = nullptr;
#else
process_pid_ = -1;
pipe_stdout_[0] = pipe_stdout_[1] = -1;
pipe_stdin_[0] = pipe_stdin_[1] = -1;
process_running_ = false;
#endif
max_log_lines_ = 1000;
stop_timeout_ms_ = 5000;
output_encoding_ = OutputEncoding::AUTO_DETECT;
use_auto_working_dir_ = false; // 自动工作目录
}
CLIProcess::~CLIProcess() {
Stop();
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) {
std::lock_guard<std::mutex> lock(encoding_mutex_);
output_encoding_ = encoding;
AddLog("输出编码已设置为: " + GetEncodingName(encoding));
}
// 获取输出编码
OutputEncoding CLIProcess::GetOutputEncoding() const {
std::lock_guard<std::mutex> lock(encoding_mutex_);
return output_encoding_;
}
// 获取编码名称
std::string CLIProcess::GetEncodingName(const OutputEncoding encoding) {
switch (encoding) {
case OutputEncoding::AUTO_DETECT: return "自动检测";
case OutputEncoding::UTF8: return "UTF-8";
#ifdef _WIN32
case OutputEncoding::GBK: return "GBK";
case OutputEncoding::GB2312: return "GB2312";
case OutputEncoding::BIG5: return "BIG5";
case OutputEncoding::SHIFT_JIS: return "Shift_JIS";
#else
case OutputEncoding::ISO_8859_1: return "ISO-8859-1";
case OutputEncoding::GB18030: return "GB18030";
case OutputEncoding::BIG5: return "BIG5";
case OutputEncoding::EUC_JP: return "EUC-JP";
#endif
default: return "未知编码";
}
}
// 获取支持的编码列表
std::vector<std::pair<OutputEncoding, std::string>> CLIProcess::GetSupportedEncodings() {
return {
{OutputEncoding::AUTO_DETECT, "自动检测"},
{OutputEncoding::UTF8, "UTF-8"},
#ifdef _WIN32
{OutputEncoding::GBK, "GBK (简体中文)"},
{OutputEncoding::GB2312, "GB2312 (简体中文)"},
{OutputEncoding::BIG5, "Big5 (繁体中文)"},
{OutputEncoding::SHIFT_JIS, "Shift-JIS (日文)"},
#else
{OutputEncoding::ISO_8859_1, "ISO-8859-1"},
{OutputEncoding::GB18030, "GB18030"},
{OutputEncoding::BIG5, "BIG5"},
{OutputEncoding::EUC_JP, "EUC-JP"},
#endif
};
}
// 根据编码获取代码页
UINT CLIProcess::GetCodePageFromEncoding(const OutputEncoding encoding) {
switch (encoding) {
case OutputEncoding::GBK: return 936;
case OutputEncoding::GB2312: return 20936;
case OutputEncoding::BIG5: return 950;
case OutputEncoding::SHIFT_JIS: return 932;
default: return CP_ACP; // 系统默认代码页
}
}
// 检查是否为有效的UTF-8
bool CLIProcess::IsValidUTF8(const std::string& str) {
const auto* bytes = reinterpret_cast<const unsigned char*>(str.c_str());
size_t len = str.length();
for (size_t i = 0; i < len; ) {
if (bytes[i] <= 0x7F) {
// ASCII字符
i++;
} else if ((bytes[i] & 0xE0) == 0xC0) {
// 2字节UTF-8序列
if (i + 1 >= len || (bytes[i + 1] & 0xC0) != 0x80) return false;
i += 2;
} else if ((bytes[i] & 0xF0) == 0xE0) {
// 3字节UTF-8序列
if (i + 2 >= len || (bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80) return false;
i += 3;
} else if ((bytes[i] & 0xF8) == 0xF0) {
// 4字节UTF-8序列
if (i + 3 >= len || (bytes[i + 1] & 0xC0) != 0x80 || (bytes[i + 2] & 0xC0) != 0x80 || (bytes[i + 3] & 0xC0) != 0x80) return false;
i += 4;
} else {
return false;
}
}
return true;
}
// 转换到UTF-8
std::string CLIProcess::ConvertToUTF8(std::string& input, const OutputEncoding encoding) {
if (input.empty()) return input;
// 如果已经是UTF-8编码直接返回
if (encoding == OutputEncoding::UTF8) {
return input;
}
UINT codePage = GetCodePageFromEncoding(encoding);
// 先转换为宽字符
int wideSize = MultiByteToWideChar(codePage, 0, input.c_str(), -1, nullptr, 0);
if (wideSize <= 0) {
// 转换失败,返回原始字符串
return input;
}
std::vector<wchar_t> wideStr(wideSize);
if (MultiByteToWideChar(codePage, 0, input.c_str(), -1, wideStr.data(), wideSize) <= 0) {
return input;
}
// 再从宽字符转换为UTF-8
int utf8Size = WideCharToMultiByte(CP_UTF8, 0, wideStr.data(), -1, nullptr, 0, nullptr, nullptr);
if (utf8Size <= 0) {
return input;
}
std::vector<char> utf8Str(utf8Size);
if (WideCharToMultiByte(CP_UTF8, 0, wideStr.data(), -1, utf8Str.data(), utf8Size, nullptr, nullptr) <= 0) {
return input;
}
return std::string(utf8Str.data());
}
// 自动检测并转换到UTF-8
std::string CLIProcess::DetectAndConvertToUTF8(std::string& input) {
if (input.empty()) return input;
// 首先检查是否已经是有效的UTF-8
if (IsValidUTF8(input)) {
return input;
}
// 尝试不同的编码进行转换
const std::vector encodingsToTry = {
OutputEncoding::GBK,
OutputEncoding::GB2312,
OutputEncoding::BIG5,
OutputEncoding::SHIFT_JIS
};
for (OutputEncoding encoding : encodingsToTry) {
std::string converted = ConvertToUTF8(input, encoding);
if (converted != input && IsValidUTF8(converted)) {
// 转换成功且结果是有效的UTF-8
return converted;
}
}
// 如果所有编码都失败,尝试使用系统默认代码页
return ConvertToUTF8(input, OutputEncoding::GBK); // 默认使用GBK
}
void CLIProcess::SetStopCommand(const std::string& command, int timeout_ms) {
std::lock_guard<std::mutex> lock(stop_mutex_);
stop_command_ = command;
stop_timeout_ms_ = (timeout_ms > 0) ? timeout_ms : 5000;
if (!command.empty()) {
AddLog("已设置停止命令: " + command + " (超时: " + std::to_string(timeout_ms) + "ms)");
} else {
}
}
void CLIProcess::SetMaxLogLines(int max_lines) {
std::lock_guard<std::mutex> lock(logs_mutex_);
max_log_lines_ = max_lines;
if (logs_.size() > max_log_lines_) {
logs_.erase(logs_.begin(), logs_.end() - max_log_lines_);
}
}
void CLIProcess::SetEnvironmentVariables(const std::map<std::string, std::string>& env_vars) {
std::lock_guard<std::mutex> lock(env_mutex_);
environment_variables_.clear();
// 验证所有环境变量
for (const auto& pair : env_vars) {
if (pair.first.empty()) {
AddLog("警告: 跳过空的环境变量名");
continue;
}
if (pair.first.find('=') != std::string::npos || pair.first.find('\0') != std::string::npos) {
AddLog("警告: 跳过包含无效字符的环境变量: " + pair.first);
continue;
}
environment_variables_[pair.first] = pair.second;
}
if (!environment_variables_.empty()) {
// AddLog("已设置 " + std::to_string(environment_variables_.size()) + " 个有效环境变量");
for (const auto& pair : environment_variables_) {
// AddLog(" " + pair.first + "=" + pair.second);
}
} else {
// AddLog("已清空所有自定义环境变量");
}
}
const std::map<std::string, std::string>& CLIProcess::GetEnvironmentVariables() const {
std::lock_guard<std::mutex> lock(env_mutex_);
return environment_variables_;
}
void CLIProcess::AddEnvironmentVariable(const std::string& key, const std::string& value) {
std::lock_guard<std::mutex> lock(env_mutex_);
// 验证环境变量名
if (key.empty()) {
AddLog("错误: 环境变量名不能为空");
return;
}
// 检查是否包含无效字符
if (key.find('=') != std::string::npos || key.find('\0') != std::string::npos) {
AddLog("错误: 环境变量名包含无效字符: " + key);
return;
}
environment_variables_[key] = value;
// AddLog("添加环境变量: " + key + "=" + value);
}
void CLIProcess::RemoveEnvironmentVariable(const std::string& key) {
std::lock_guard<std::mutex> lock(env_mutex_);
auto it = environment_variables_.find(key);
if (it != environment_variables_.end()) {
environment_variables_.erase(it);
// AddLog("移除环境变量: " + key);
}
}
void CLIProcess::ClearEnvironmentVariables() {
std::lock_guard<std::mutex> lock(env_mutex_);
environment_variables_.clear();
// AddLog("已清空所有自定义环境变量");
}
void CLIProcess::Start(const std::string& command) {
Stop();
// 确定工作目录
std::wstring working_dir;
{
std::lock_guard<std::mutex> lock(working_dir_mutex_);
if (use_auto_working_dir_) {
working_dir = StringToWide(ExtractDirectoryFromCommand(command));
if (working_dir.empty()) {
working_dir = StringToWide(std::filesystem::current_path().string());
}
// AddLog("自动检测工作目录: " + working_dir);
} else {
working_dir = StringToWide(working_directory_);
if (!working_dir.empty()) {
// AddLog("使用指定工作目录: " + working_dir);
}
}
}
#ifdef _WIN32
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = nullptr;
if (!CreatePipe(&hReadPipe_, &hWritePipe_, &sa, 0)) {
AddLog("创建输出管道失败");
return;
}
if (!CreatePipe(&hReadPipe_stdin_, &hWritePipe_stdin_, &sa, 0)) {
AddLog("创建输入管道失败");
CloseHandle(hReadPipe_);
CloseHandle(hWritePipe_);
return;
}
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdOutput = hWritePipe_;
si.hStdError = hWritePipe_;
si.hStdInput = hReadPipe_stdin_;
si.wShowWindow = SW_HIDE;
ZeroMemory(&pi_, sizeof(pi_));
// 转换命令为宽字符
std::wstring wcmd = StringToWide(command);
// CreateProcess需要可修改的字符串
std::vector<wchar_t> cmdBuffer(wcmd.begin(), wcmd.end());
cmdBuffer.push_back(L'\0');
// 使用Windows API设置环境变量
std::vector<std::pair<std::string, std::string>> originalEnvVars;
bool envVarsSet = false;
if (!environment_variables_.empty()) {
envVarsSet = true;
for (const auto &pair: environment_variables_) {
if (!pair.first.empty()) {
// 保存原始值(如果存在)
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, "");
}
// 设置新的环境变量值
if (SetEnvironmentVariableA(pair.first.c_str(), pair.second.c_str())) {
// AddLog("设置环境变量: " + pair.first + "=" + pair.second);
} else {
AddLog("设置环境变量失败: " + pair.first + " (错误代码: " + std::to_string(GetLastError()) + ")");
}
}
}
// AddLog("环境变量设置完成,数量: " + std::to_string(environment_variables_.size()));
} else {
AddLog("未设置自定义环境变量,使用默认环境 PWD:" + WideToString(working_dir));
}
BOOL result = CreateProcess(
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 (envVarsSet) {
for (const auto &pair: originalEnvVars) {
if (pair.second.empty()) {
// 原来不存在,删除变量
SetEnvironmentVariableA(pair.first.c_str(), nullptr);
} else {
// 恢复原始值
SetEnvironmentVariableA(pair.first.c_str(), pair.second.c_str());
}
}
}
if (result) {
AddLog("进程已启动: " + command + " PID: " + std::to_string(pi_.dwProcessId));
CloseHandle(hWritePipe_);
CloseHandle(hReadPipe_stdin_);
hWritePipe_ = nullptr;
hReadPipe_stdin_ = nullptr;
output_thread_ = std::thread([this]() {
ReadOutput();
});
} 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;
}
#else
// Unix/Linux implementation
int pipe_out[2];
int pipe_in[2];
if (pipe(pipe_out) < 0 || pipe(pipe_in) < 0) {
AddLog("创建管道失败: " + std::string(strerror(errno)));
return;
}
pid_t pid = fork();
if (pid == 0) {
// child process
close(pipe_out[0]);
dup2(pipe_out[1], STDOUT_FILENO);
dup2(pipe_out[1], STDERR_FILENO);
close(pipe_out[1]);
close(pipe_in[1]);
dup2(pipe_in[0], STDIN_FILENO);
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
{
std::lock_guard<std::mutex> lock(env_mutex_);
if (!environment_variables_.empty()) {
for (const auto& kv : environment_variables_) {
setenv(kv.first.c_str(), kv.second.c_str(), 1);
}
}
}
execl("/bin/sh", "sh", "-c", command.c_str(), (char*)nullptr);
_exit(127);
}
else if (pid > 0) {
// parent process
close(pipe_out[1]);
close(pipe_in[0]);
process_pid_ = pid;
pipe_stdout_[0] = pipe_out[0];
pipe_stdout_[1] = pipe_out[1]; // closed already in parent, but keep for safety
pipe_stdin_[0] = pipe_in[0]; // closed already in parent, but keep for safety
pipe_stdin_[1] = pipe_in[1];
process_running_ = true;
AddLog("进程已启动PID: " + std::to_string(pid));
if (!working_dir.empty()) {
AddLog("工作目录: " + working_dir);
}
// Start output reading thread
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
}
void CLIProcess::Stop() {
#ifdef _WIN32
std::lock_guard<std::mutex> lock(stop_mutex_);
if (pi_.hProcess != nullptr) {
if (!stop_command_.empty()) {
SendCommand(stop_command_);
// Wait for process to exit within timeout
WaitForSingleObject(pi_.hProcess, stop_timeout_ms_);
}
TerminateProcess(pi_.hProcess, 0);
WaitForSingleObject(pi_.hProcess, INFINITE);
CloseProcessHandles();
}
#else
if (process_running_) {
std::lock_guard<std::mutex> lock(stop_mutex_);
if (!stop_command_.empty()) {
SendCommand(stop_command_);
// Wait for termination or timeout
int status = 0;
for (int i = 0; i < stop_timeout_ms_/100; ++i) {
pid_t result = waitpid(process_pid_, &status, WNOHANG);
if (result == process_pid_) {
process_running_ = false;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
if (process_running_) {
kill(process_pid_, SIGKILL);
waitpid(process_pid_, nullptr, 0);
process_running_ = false;
}
CloseProcessHandles();
}
#endif
if (output_thread_.joinable()) {
output_thread_.join();
}
}
// 关闭进程句柄的辅助函数
void CLIProcess::CloseProcessHandles() {
if (pi_.hProcess) {
CloseHandle(pi_.hProcess);
pi_.hProcess = nullptr;
}
if (pi_.hThread) {
CloseHandle(pi_.hThread);
pi_.hThread = nullptr;
}
}
// 清理资源的辅助函数
void CLIProcess::CleanupResources() {
// 关闭输入管道写入端(通知进程停止)
if (hWritePipe_stdin_) {
CloseHandle(hWritePipe_stdin_);
hWritePipe_stdin_ = nullptr;
}
// 等待输出线程结束
if (output_thread_.joinable()) {
output_thread_.join();
}
// 关闭输出管道读取端
if (hReadPipe_) {
CloseHandle(hReadPipe_);
hReadPipe_ = nullptr;
}
// 确保所有句柄都已关闭
if (hWritePipe_) {
CloseHandle(hWritePipe_);
hWritePipe_ = nullptr;
}
if (hReadPipe_stdin_) {
CloseHandle(hReadPipe_stdin_);
hReadPipe_stdin_ = nullptr;
}
}
void CLIProcess::Restart(const std::string& command) {
Stop();
Start(command);
}
void CLIProcess::ClearLogs() {
std::lock_guard<std::mutex> lock(logs_mutex_);
logs_.clear();
}
void CLIProcess::AddLog(const std::string& log) {
std::lock_guard<std::mutex> lock(logs_mutex_);
logs_.push_back(log);
if (logs_.size() > max_log_lines_) {
logs_.erase(logs_.begin(), logs_.begin() + (logs_.size() - max_log_lines_));
}
}
const std::vector<std::string>& CLIProcess::GetLogs() const {
std::lock_guard<std::mutex> lock(logs_mutex_);
return logs_;
}
bool CLIProcess::SendCommand(const std::string &command) {
#ifdef _WIN32
if (!IsRunning() || !hWritePipe_stdin_) {
return false;
}
DWORD bytesWritten;
std::string fullCommand = command + "\n";
if (WriteFile(hWritePipe_stdin_, fullCommand.c_str(),
static_cast<DWORD>(fullCommand.length()), &bytesWritten, nullptr)) {
AddLog("> " + command);
return true;
}
return false;
#else
if (!process_running_ || pipe_stdin_[1] < 0) return false;
std::string cmd = command + "\n";
ssize_t written = write(pipe_stdin_[1], cmd.c_str(), cmd.size());
return written == (ssize_t)cmd.size();
#endif
}
void CLIProcess::CopyLogsToClipboard() const {
#ifdef _WIN32
std::lock_guard<std::mutex> lock(logs_mutex_);
if (logs_.empty()) return;
// 构建完整的日志字符串(使用\r\n确保跨平台兼容
std::wstring allLogs;
for (const auto& log : logs_) {
allLogs += StringToWide(log) ;
allLogs.resize(allLogs.size() - sizeof(wchar_t));
allLogs += L"\n";
}
if (OpenClipboard(nullptr)) {
EmptyClipboard();
// 计算正确的内存大小(包括终止空字符)
const size_t dataSize = (allLogs.length() + 1) * sizeof(wchar_t);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, dataSize);
if (hMem) {
wchar_t* pMem = static_cast<wchar_t*>(GlobalLock(hMem));
if (pMem) {
// 安全复制宽字符串数据
wcscpy_s(pMem, allLogs.length() + 1, allLogs.c_str());
GlobalUnlock(hMem);
SetClipboardData(CF_UNICODETEXT, hMem);
} else {
GlobalFree(hMem); // 锁定失败时释放内存
}
}
CloseClipboard();
}
#else
// Unix / macOS, use xclip or pbcopy
std::string clipboard_text;
{
std::lock_guard<std::mutex> lock(logs_mutex_);
for (const auto& line : logs_) {
clipboard_text += line + "\n";
}
}
FILE* pipe = popen("pbcopy", "w");
if (!pipe) {
pipe = popen("xclip -selection clipboard", "w");
}
if (pipe) {
fwrite(clipboard_text.c_str(), 1, clipboard_text.size(), pipe);
pclose(pipe);
}
#endif
}
bool CLIProcess::IsRunning() const {
#ifdef _WIN32
if (pi_.hProcess == nullptr) return false;
DWORD status = WaitForSingleObject(pi_.hProcess, 0);
return status == WAIT_TIMEOUT;
#else
if (!process_running_) return false;
int status;
pid_t result = waitpid(process_pid_, &status, WNOHANG);
if (result == 0) return true; // still running
process_running_ = false;
return false;
#endif
}
void CLIProcess::ReadOutput() {
#ifdef _WIN32
constexpr int BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
DWORD bytesRead;
std::string partialLine;
while (true) {
if (!ReadFile(hReadPipe_, buffer, BUFFER_SIZE - 1, &bytesRead, nullptr) || bytesRead == 0) {
break;
}
buffer[bytesRead] = '\0';
std::string output(buffer);
// 根据设置的编码转换输出
OutputEncoding currentEncoding;
{
std::lock_guard<std::mutex> lock(encoding_mutex_);
currentEncoding = output_encoding_;
}
std::string convertedOutput;
if (currentEncoding == OutputEncoding::AUTO_DETECT) {
convertedOutput = DetectAndConvertToUTF8(output);
} else {
convertedOutput = ConvertToUTF8(output, currentEncoding);
}
size_t start = 0;
size_t end = convertedOutput.find('\n');
while (end != std::string::npos) {
std::string line = partialLine + convertedOutput.substr(start, end - start);
partialLine.clear();
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) {
AddLog(line);
}
start = end + 1;
end = convertedOutput.find('\n', start);
}
if (start < convertedOutput.size()) {
partialLine = convertedOutput.substr(start);
}
}
#else
const int buffer_size = 4096;
char buffer[buffer_size];
ssize_t bytes_read = 0;
while (process_running_) {
bytes_read = read(pipe_stdout_[0], buffer, buffer_size - 1);
if (bytes_read <= 0) break;
buffer[bytes_read] = '\0';
std::string utf8_str;
{
std::lock_guard<std::mutex> lock(encoding_mutex_);
if (output_encoding_ == OutputEncoding::AUTO_DETECT) {
utf8_str = DetectAndConvertToUTF8(std::string(buffer));
} else {
utf8_str = ConvertToUTF8(std::string(buffer), output_encoding_);
}
}
AddLog(utf8_str);
}
#endif
}
std::wstring CLIProcess::GetPid() const {
if (pi_.hProcess == nullptr) return L"";
return StringToWide(std::to_string(pi_.dwProcessId));
}