Qt实现本地服务管理
前言
本节将使用Windows的几个API,使用qt实现 本地服务 简单管理。这里简单介绍下服务。
几乎每一种操作系统都有一种在系统启动时启动的进程机制,这种机制不会依赖于用户的交互。在Windows下,类似的基础称为Windows服务。服务是一种程序类型,它在后台运行,服务程序通常可以在本地和通过网络为用户提供一些功能,服务在操作系统启动时就会随之启动的程序。
效果
实现效果和Windows 服务管理类似,等会我们用我们写的软件进行服务的启动和暂停,然后刷新windows的服务管理 和我们的操作结果应该是一致的。
相关的Windows API
- 打开服务管理句柄
SC_HANDLE OpenSCManagerA(
LPCSTR lpMachineName, // computer name,NULL 为 本机
LPCSTR lpDatabaseName, // SCM database name,应该为 SERVICES_ACTIVE_DATABASE,NULL默认也为前面那个
DWORD dwDesiredAccess // 对scm数据库的访问权限 SC_MANAGER_ALL_ACCESS
);
// 成功返回service control manager database的handle
// 对应的关闭函数
BOOL CloseServiceHandle(
SC_HANDLE hSCObject
);
- 枚举服务
BOOL EnumServicesStatusExA(
SC_HANDLE hSCManager, // OpenSCManager函数返回的句柄
SC_ENUM_TYPE InfoLevel, // SC_ENUM_PROCESS_INFO
DWORD dwServiceType,// 枚举的服务类型
DWORD dwServiceState, // 枚举指定状态的服务
LPBYTE lpServices, // 指向ENUM_SERVICE_STATUS_PROCESSA类型的指针
DWORD cbBufSize, // The size of the buffer pointed to by the lpServices parameter, in bytes.
LPDWORD pcbBytesNeeded, // 传出参数,当buffer太小,返回实际需要的大小
LPDWORD lpServicesReturned,// 传出参数,返回枚举服务的个数
LPDWORD lpResumeHandle, // 输入输出参数,返回枚举是否成功
LPCSTR pszGroupName // 加载 组名字
);
成功返回非0
- 打开服务
SC_HANDLE OpenServiceA(
SC_HANDLE hSCManager,
LPCSTR lpServiceName, // 要打开的服务的名称
DWORD dwDesiredAccess // 访问的权限
);
- 启动服务
BOOL StartServiceA(
SC_HANDLE hService, // 启动服务的句柄,有上面获得
DWORD dwNumServiceArgs, // 启动服务的参数个数
LPCSTR *lpServiceArgVectors // 启动服务的参数
);
- 停止服务
BOOL ControlService(
SC_HANDLE hService,
DWORD dwControl, // 控制码
LPSERVICE_STATUS lpServiceStatus // 传出参数,返回服务的状态
);
核心代码
刷新表格代码:
void Widget::refreshTable(){
int count = ui->serviceTable->rowCount();
// 先清空表
for(int i = 0; i < count;i++)
ui->serviceTable->removeRow(0);
// 清空之前申请的空间
if(mBuf!=nullptr)
delete[] mBuf;
DWORD needLen,serviceNum,result,type;
BOOL ret;
// 刷新表格,枚举服务
if(ui->win32->isChecked()){
// win32服务
type = SERVICE_WIN32;
}else{
// 驱动服务
type = SERVICE_DRIVER;
}
// 第一次 获取需要的字节长度
ret = EnumServicesStatusExA(mHSCM,SC_ENUM_PROCESS_INFO ,type
,SERVICE_STATE_ALL,NULL,0,&needLen
,&serviceNum,NULL,NULL);
mBuf = new char[needLen]{0};
if( ret == 0){
// 下面这段switch代码要写,在qt5.9.7版本下 不写就是不行
switch (GetLastError()) {
case ERROR_ACCESS_DENIED:
qDebug() << "ERROR_ACCESS_DENIED";
break;
case ERROR_MORE_DATA:
qDebug() << "ERROR_MORE_DATA";
break;
case ERROR_INVALID_PARAMETER:
qDebug() << "ERROR_INVALID_PARAMETER";
break;
case ERROR_INVALID_HANDLE:
qDebug() << "ERROR_INVALID_HANDLE";
break;
case ERROR_INVALID_LEVEL:
qDebug() << "ERROR_INVALID_LEVEL";
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
qDebug() << "ERROR_SHUTDOWN_IN_PROGRESS";
break;
default:
qDebug() << "other error";
break;
}
// EnumServicesStatusExA 枚举服务API
ret = EnumServicesStatusExA(mHSCM,SC_ENUM_PROCESS_INFO ,type
,SERVICE_STATE_ALL,reinterpret_cast<LPBYTE>(mBuf),needLen,&needLen
,&serviceNum,&result,NULL);
LPENUM_SERVICE_STATUS_PROCESSA p = reinterpret_cast<LPENUM_SERVICE_STATUS_PROCESSA>(mBuf);
if(ret == 0){
qDebug() << "获取服务失败";
}else{
qDebug() << "获取服务成功";
// 往表格中添加数据
for(DWORD i = 0; i < serviceNum; i++){
ui->serviceTable->insertRow(i);
ui->serviceTable->setItem(i,0,new QTableWidgetItem(QString(p[i].lpServiceName)));
ui->serviceTable->setItem(i,1,new QTableWidgetItem(QString::fromLocal8Bit(p[i].lpDisplayName)));
QTableWidgetItem* item;
switch(p[i].ServiceStatusProcess.dwCurrentState){
case SERVICE_PAUSED:
item = new QTableWidgetItem("暂停");
break;
case SERVICE_STOPPED:
item = new QTableWidgetItem("停止");
break;
case SERVICE_RUNNING:
item = new QTableWidgetItem("运行");
break;
default:
item = new QTableWidgetItem("其他");
break;
}
ui->serviceTable->setItem(i,2,item);
}
// delete
}
}
}
启动服务
void Widget::on_startBtn_clicked()
{
// 启动服务
int row = ui->serviceTable->currentRow();
LPENUM_SERVICE_STATUS_PROCESSA p = reinterpret_cast<LPENUM_SERVICE_STATUS_PROCESSA>(mBuf);
if(p[row].ServiceStatusProcess.dwCurrentState == SERVICE_RUNNING)
return;
const char* name = ui->serviceTable->item(row,0)->text().toStdString().c_str();
SC_HANDLE service = OpenServiceA(mHSCM, name, SERVICE_ALL_ACCESS);
if(service == nullptr){
qDebug() << "打开服务失败";
return;
}
// StartServiceA启动服务API
BOOL ret = StartServiceA(service,0,nullptr);
if( ret ){
qDebug() << "启动服务成功";
ui->serviceTable->setItem(row,2,new QTableWidgetItem("运行"));
p[row].ServiceStatusProcess.dwCurrentState = SERVICE_RUNNING;
}else{
qDebug() << "启动服务失败";
}
}
停止服务
void Widget::on_stopBtn_clicked()
{
// 停止服务
int row = ui->serviceTable->currentRow();
LPENUM_SERVICE_STATUS_PROCESSA p = reinterpret_cast<LPENUM_SERVICE_STATUS_PROCESSA>(mBuf);
if(p[row].ServiceStatusProcess.dwCurrentState == SERVICE_STOPPED)
return;
const char* name = ui->serviceTable->item(row,0)->text().toStdString().c_str();
if( name == nullptr)
return;
SC_HANDLE service = OpenServiceA(mHSCM, name, SERVICE_ALL_ACCESS);
if(service == nullptr){
qDebug() << "打开服务失败";
return;
}
SERVICE_STATUS status;
// StartServiceA停止服务API
BOOL ret = ControlService(service,SERVICE_CONTROL_STOP,&status);
if( ret ){
qDebug() << "停止服务成功";
ui->serviceTable->setItem(row,2,new QTableWidgetItem("停止"));
p[row].ServiceStatusProcess.dwCurrentState = SERVICE_STOPPED;
}else{
qDebug() << "停止服务失败";
}
}