一、逻辑分析
1、windows没有fork函数,无法创建僵尸进程,使用打开非子进程的方式打开外部进程---打开外部进程
2、守护进程应该是单例的进程---》单例进程
3、守护进程应该是存在一个子线程不断的判断是否存在守护目标,若不存在(异常退出)则启动守护目标
4、当守护目标正常退出时,需要关闭守护进程
(1)、直接kill掉守护进程(守护进程没有做成病毒进程(系统进程)---记录下这里没找到方法)
(2)、进程间通讯实现关闭守护进程---有很多的方法博主使用的是命名管道
二、守护线程
#pragma once
#include <QThread>
class HiDaemon : public QThread
{
Q_OBJECT
public:
HiDaemon(QObject *parent = nullptr);
~HiDaemon();
public slots:
void start();
void stop();
private:
void run();
private:
volatile bool m_bRunning;
QString m_strFilePath;
QString m_strProducerName;
};
#include "HiDaemon.h"
#include <windows.h>
#include "tlhelp32.h"
#include "tchar.h"
#include "HiDaemonDef.h"
#include "HiUtils.h"
#include <QMessageBox>
HiDaemon::HiDaemon(QObject *parent)
: QThread(parent)
, m_bRunning(false)
, m_strProducerName("") // 这里自定义默认值
{
m_strFilePath = QApplication::applicationDirPath();
QStringList strList = QApplication::arguments();
if (strList.size() > 1)
{
m_strProducerName = strList.at(1);
}
}
HiDaemon::~HiDaemon()
{
}
void HiDaemon::start()
{
if (m_bRunning)
{
return;
}
m_bRunning = true;
QThread::start();
}
void HiDaemon::stop()
{
m_bRunning = false;
wait(1000);
}
void HiDaemon::run()
{
STARTUPINFOA si;
//进程对象
PROCESS_INFORMATION pi;
//初始化
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
//构造cmd执行守护进程的字符串
char pCmd[MAX_PATH_NUM] = { 0 };
{
QFileInfo file(m_strFilePath + QDir::separator() + m_strProducerName);
QString strFilePath = file.filePath();
strcat_s(pCmd, strFilePath.toStdString().c_str());
}
while (m_bRunning)
{
//检查守护程序是否存在
pi.hProcess = GetProcessHandleByName(m_strProducerName);
if (pi.hProcess == nullptr)
{
// 创建子进程,判断是否执行成功
if (CreateProcessA(NULL, pCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
// 创建失败
}
}
if (pi.hProcess != nullptr)
{
//无限等待子进程退出
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
Sleep(100);
}
}
二、命名管道实时通讯
#pragma once
#include <windows.h>
#include <QThread>
//
//HANDLE CreateNamedPipe(
// LPCTSTR lpName, // pipe name
// DWORD dwOpenMode, // pipe open mode
// DWORD dwPipeMode, // pipe-specific modes
// DWORD nMaxInstances, // maximum number of instances
// DWORD nOutBufferSize, // output buffer size
// DWORD nInBufferSize, // input buffer size
// DWORD nDefaultTimeOut, // time-out interval
// LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD
// );
//
class HiNamePipeServer : public QThread
{
Q_OBJECT
public:
HiNamePipeServer(QObject *parent = nullptr);
~HiNamePipeServer();
void start();
void stop();
private slots:
void onParse();
private:
void creatPipe();
void run();
signals:
void parse();
void stopDaemon();
private:
HANDLE m_pHandle;
HANDLE m_pEvent;
volatile bool m_bRunning;
char m_readbuffer[100];
DWORD m_readLenth;
bool m_resetStarted;
QString m_strFilePath;
QString m_strProducerName;
};
#include "HiNamePipeServer.h"
#include <windows.h>
#include <QDebug>
#include "HiDaemonDef.h"
#include "HiUtils.h"
HiNamePipeServer::HiNamePipeServer(QObject *parent)
: QThread(parent)
, m_bRunning(false)
, m_pHandle(nullptr)
, m_resetStarted(false)
{
creatPipe();
connect(this, &HiNamePipeServer::parse, this, &HiNamePipeServer::onParse, Qt::DirectConnection);
m_strFilePath = QApplication::applicationDirPath();
QStringList strList = QApplication::arguments();
if (strList.size() > 1)
{
m_strProducerName = strList.at(1);
}
}
HiNamePipeServer::~HiNamePipeServer()
{
if (m_pHandle)
{
CloseHandle(m_pHandle);
}
CloseHandle(m_pEvent);
}
void HiNamePipeServer::start()
{
if (m_bRunning)
{
return;
}
m_bRunning = true;
QThread::start();
}
void HiNamePipeServer::stop()
{
m_bRunning = false;
}
void HiNamePipeServer::onParse()
{
QString strMsg = m_readbuffer;
QStringList paramList = strMsg.split(WRITE_SPACER);
if (paramList.size() != 2)
{
return;
}
PipeWriteType writeType = PipeWriteType(paramList.at(0).toInt());
switch (writeType)
{
case PipeWriteType_ClientExit:
{
emit stopDaemon();
qApp->quit();
break;
}
case PipeWriteType_ClinetRestart:
{
if (m_resetStarted == false)
{
m_resetStarted = true;
KillProcess(m_strProducerName);
}
break;
}
default:
break;
}
}
void HiNamePipeServer::creatPipe()
{
m_pHandle = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_MAXINSTANCES, 0, 0, 1000, NULL);
if (INVALID_HANDLE_VALUE == m_pHandle)
{
CloseHandle(m_pHandle);
// 创建管道失败
m_pHandle = nullptr;
}
}
void HiNamePipeServer::run()
{
// 这里暂时同步调用,可以考虑使用异步设置超时事件
while (m_bRunning)
{
OVERLAPPED ovlap;
ZeroMemory(&ovlap, sizeof(OVERLAPPED));
//2. 创建管道连接
if (!ConnectNamedPipe(m_pHandle, &ovlap))
{
if (ERROR_IO_PENDING != GetLastError())
{
Sleep(100);
continue;
}
}
// 这个等待可能非常的久
if (ReadFile(m_pHandle, m_readbuffer, 100, &m_readLenth, NULL) == TRUE)
{
emit parse();
}
DisconnectNamedPipe(m_pHandle);
}
}
管道读取线程是阻塞的
三、主函数
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <iostream>
#include <string>
#include <QString>
#include <QApplication>
#include "HiNamePipeServer.h"
#include "HiDaemon.h"
#include <QObject>
#include "HiSingleApplication.h"
LPCTSTR szAppClassName = TEXT("HiDaemon");
LPCTSTR szAppWindowName = TEXT("HiDaemon");
// HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessID);
// ::TerminateProcess(hProcess, 0);
// CloseHandle(hProcess);
using namespace std;
// 隐藏DOS黑窗口
#ifdef _MSC_VER
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
#endif
int main(int argc, char* argv[])
{
HiSingleApplication a(argc, argv);
// 这里应该传入的产品类型,不同的产品,启动后可能动作不一致
QCoreApplication::setOrganizationName("EnlightV");
QCoreApplication::setApplicationName("HiDaemon");
if (a.isRunning())
{
return 0;
}
HiDaemon *daemon = new HiDaemon;
HiNamePipeServer *server = new HiNamePipeServer;
QObject::connect(server, &HiNamePipeServer::stopDaemon, daemon, &HiDaemon::stop, Qt::DirectConnection);
daemon->start();
server->start();
a.exec();
return 0;
}
四、应用程序启动守护进程
// 获取守护进程句柄
HANDLE GetProcessHandleByName(const QString &name)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL fOk;
for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
{
if (QString::fromWCharArray(pe.szExeFile) == name)
{
CloseHandle(hSnapshot);
return GetProcessHandle(pe.th32ProcessID);
}
}
return NULL;
}
//tempPath 是自定义的一个可写目录
void StartDaemon(const QString &tempPath)
{
// 1、检测守护进程是否已经启动了
if (GetProcessHandleByName(DAEMON_NAME) != nullptr)
{
return;
}
QString batFile = tempPath + QDir::separator() + "StartDaemon.bat";
if (QFile(batFile).exists())
{
QFile(batFile).remove();
}
QFile file(batFile);
if (file.open(QFile::ReadWrite))
{
QString strParam = QString("@echo off \n start /d \"%1\" HiDaemond.exe \"%2\" \n exit").arg(QCoreApplication::applicationDirPath()).arg(QFileInfo(QCoreApplication::applicationFilePath()).fileName());
file.write(strParam.toLocal8Bit());
file.close();
QProcess p;
p.start("cmd.exe", QStringList() << "/c" << batFile);
if (p.waitForStarted())
{
p.waitForFinished(2000);
return;
}
}
else
{
QString strAppPath = QApplication::applicationDirPath();
QStringList arguments;
{
QFileInfo file(QApplication::applicationFilePath());
arguments.append(file.fileName());
}
QFileInfo deamonFile(strAppPath + QDir::separator() + DAEMON_NAME);
if (deamonFile.exists())
{
std::wstring operate = QString("runas").toStdWString();//临时提升管理员权限
std::wstring path = QCoreApplication::applicationDirPath().toStdWString();
std::wstring appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName().toStdWString();;
std::wstring file = deamonFile.filePath().toStdWString();
int ret = (int)ShellExecute(NULL, operate.c_str(), file.c_str(), appName.c_str(), path.c_str(), SW_SHOWNORMAL);
}
}
}
五、主端关闭时候,通讯给守护进程自行关闭
#pragma once
#include <windows.h>
class HiNamePipeClient
{
public:
static HiNamePipeClient* getInstance();
void writeFile(PipeWriteType type, const QString & param = QString());
protected:
HiNamePipeClient();
~HiNamePipeClient();
private:
bool openPipe();
private:
HANDLE m_pHandle;
};
inline HiNamePipeClient *pipeClient()
{
return HiNamePipeClient::getInstance();
}
#include "HiNamePipeClient.h"
#include "guard.h"
enum PipeWriteType
{
PipeWriteType_ClientExit = 0, // 客户端正常退出
PipeWriteType_ClinetRestart, // 客户端重启
};
HiNamePipeClient* HiNamePipeClient::getInstance()
{
static HiNamePipeClient instance;
return &instance;
}
void HiNamePipeClient::writeFile(PipeWriteType type, const QString & param)
{
#if HIRENDER_C_SERIES
if (openPipe())
{
DWORD dwWriteLen = 0;
QString strParam = QString("%1#%2").arg((int)type).arg(param);
if (WriteFile(m_pHandle, strParam.toStdString().c_str(), strParam.length(), &dwWriteLen, NULL))
{
// 写入成功
}
CloseHandle(m_pHandle);
m_pHandle = nullptr;
}
Sleep(200);
#endif
}
HiNamePipeClient::HiNamePipeClient()
: m_pHandle(nullptr)
{
}
HiNamePipeClient::~HiNamePipeClient()
{
}
bool HiNamePipeClient::openPipe()
{
if (m_pHandle)
{
CloseHandle(m_pHandle);
}
if (!WaitNamedPipe(PIPE_NAME, PIPE_TIMEOUT))
{
// 当前没有可利用的命名管道实例!
return false;
}
m_pHandle = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == m_pHandle)
{
// "打开命名管道失败!
m_pHandle = NULL;
return false;
}
return true;
}
六、强制关闭守护进程(提供方法、博主程序中未使用)
bool KillProcess(QString ProcessName)
{
QProcess p;
QString c = "taskkill /im " + ProcessName + " /f"; //exeFileName为要杀死的进程名
p.execute(c);
p.close();
return true;
}