Files
eUi/include/RpcExeProcessThread.h
RCiRoot e3f7733316 V20260829-2.6.1
完成框架目录和模块重构;新增核心、单例、工具、加密及框架 SDK;统一任务、相机和 RPC 接口,并增加离线检测、相机标定、备份恢复、版本管理、崩溃守护和心跳监控功能。
2026-09-20 16:01:09 +08:00

203 lines
5.8 KiB
C++

#pragma once
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QMutex>
#include <QMutexLocker>
#include <QProcess>
#include <QString>
#include <QStringList>
#include <QThread>
#include <QTimer>
#include <QWaitCondition>
#include <atomic>
#include <functional>
#include <windows.h>
class RpcExeProcessThread : public QThread {
public:
explicit RpcExeProcessThread(QObject* parent = nullptr)
: QThread(parent) {}
~RpcExeProcessThread() override {
stopExe();
}
bool startExe(const QString& exePath, const QStringList& args = QStringList()) {
if (isRunning()) {
return true;
}
exePath_ = resolvePath(exePath);
args_ = args;
stopping_.store(false);
{
QMutexLocker locker(&stateMutex_);
startAttempted_ = false;
startedOk_ = false;
startError_.clear();
}
if (!QFileInfo::exists(exePath_)) {
qCritical() << "RPC server exe not found:" << exePath_;
return false;
}
start();
QMutexLocker locker(&stateMutex_);
if (!startAttempted_) {
startCondition_.wait(&stateMutex_, 1500);
}
if (!startedOk_) {
qWarning() << "RPC server process did not start:" << exePath_ << startError_;
}
return startedOk_;
}
void stopExe(int timeoutMs = 1500) {
stopping_.store(true);
if (isRunning() && !wait(timeoutMs)) {
qWarning() << "RPC server process thread stop timeout:" << exePath_;
}
}
QString exePath() const {
return exePath_;
}
protected:
void run() override {
QProcess process;
HANDLE jobHandle = nullptr;
auto closeJobHandle = [&]() {
if (jobHandle) {
CloseHandle(jobHandle);
jobHandle = nullptr;
}
};
auto assignProcessToJob = [&]() {
closeJobHandle();
const DWORD pid = process.processId();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) {
qWarning() << "Failed to open RPC server process:" << exePath_;
return;
}
jobHandle = CreateJobObject(nullptr, nullptr);
if (!jobHandle) {
qWarning() << "Failed to create job object";
CloseHandle(hProcess);
return;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(jobHandle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
if (!AssignProcessToJobObject(jobHandle, hProcess)) {
qWarning() << "Failed to assign process to job object";
}
CloseHandle(hProcess);
};
std::function<void()> startChild;
startChild = [&]() {
if (stopping_.load()) {
quit();
return;
}
if (process.state() != QProcess::NotRunning) {
return;
}
process.setProgram(exePath_);
process.setArguments(args_);
process.start();
const bool ok = process.waitForStarted(1000);
{
QMutexLocker locker(&stateMutex_);
startAttempted_ = true;
startedOk_ = ok;
startError_ = ok ? QString() : process.errorString();
startCondition_.wakeAll();
}
if (!ok) {
qWarning() << "Process failed to start:" << exePath_ << process.errorString();
if (!stopping_.load()) {
QTimer::singleShot(1000, &process, startChild);
}
return;
}
qDebug() << "RPC server process started:" << exePath_;
assignProcessToJob();
};
QObject::connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
&process, [&](int exitCode, QProcess::ExitStatus exitStatus) {
qWarning() << "RPC server process finished:" << exePath_
<< "exitCode:" << exitCode << "exitStatus:" << exitStatus;
closeJobHandle();
if (stopping_.load()) {
quit();
}
else {
QTimer::singleShot(1000, &process, startChild);
}
});
QTimer stopTimer;
stopTimer.setInterval(50);
QObject::connect(&stopTimer, &QTimer::timeout, &process, [&]() {
if (!stopping_.load()) {
return;
}
if (process.state() == QProcess::Running) {
process.kill();
process.waitForFinished(500);
}
closeJobHandle();
quit();
});
stopTimer.start();
startChild();
exec();
if (process.state() == QProcess::Running) {
process.kill();
process.waitForFinished(500);
}
closeJobHandle();
}
private:
QString resolvePath(const QString& exePath) const {
QFileInfo exeInfo(exePath);
if (!exeInfo.isRelative()) {
return exePath;
}
QString resolved = QCoreApplication::applicationDirPath() + "/" + exePath;
if (!QFileInfo::exists(resolved)) {
resolved = QDir::currentPath() + "/" + exePath;
}
return resolved;
}
QString exePath_;
QStringList args_;
std::atomic<bool> stopping_{ true };
QMutex stateMutex_;
QWaitCondition startCondition_;
bool startAttempted_ = false;
bool startedOk_ = false;
QString startError_;
};