1. 项目概述
最近在开发一个Windows平台的系统工具时,需要实现对运行中进程的管理功能。经过调研,我决定使用Qt框架结合C++来实现这个进程管理器。这个工具不仅能查看当前系统的所有进程列表,还可以终止指定进程以及调整进程优先级,对于开发者调试和系统维护都非常实用。
作为一个长期使用Qt的开发者,我发现虽然Qt本身是跨平台的,但在处理系统级操作时还是需要针对不同平台进行特殊处理。这次我选择先实现Windows版本,因为Windows的进程管理API相对成熟且文档完善。下面我就来详细分享这个项目的实现过程和关键代码。
2. 环境准备与工程配置
2.1 开发环境搭建
首先需要安装以下组件:
- Qt 5.15或更高版本(我使用的是Qt 5.15.2)
- Qt Creator作为IDE
- Windows SDK(已包含所需系统头文件)
- MinGW或MSVC编译器(我选择的是MinGW 8.1.0)
提示:如果使用MSVC编译器,需要确保安装了对应的Windows SDK版本。MinGW则通常已经包含了基本的Windows API支持。
2.2 工程文件配置
Qt项目使用.pro文件进行工程配置,这是整个项目的基础。我的ProcessManager.pro文件内容如下:
qmake复制QT += core gui widgets
TARGET = ProcessManager
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp \
processmanager.cpp
HEADERS += mainwindow.h \
processmanager.h
FORMS += mainwindow.ui
# 仅Windows平台支持
win32 {
LIBS += -lpsapi -ladvapi32
}
关键配置说明:
QT += core gui widgets:指定项目需要链接的Qt模块,core和gui是基础模块,widgets用于GUI界面TARGET:指定生成的可执行文件名SOURCES/HEADERS/FORMS:分别指定源文件、头文件和UI表单文件win32块:只在Windows平台生效的配置,链接了psapi和advapi32这两个系统库
注意:psapi.dll提供了进程状态API,advapi32.dll则包含权限管理和进程优先级设置相关的API。
3. 核心功能实现
3.1 进程信息获取
进程管理器的核心功能之一是获取当前系统的进程列表。在Windows平台,我们可以使用EnumProcesses函数配合其他API来实现。
首先在processmanager.h中定义必要的结构和函数:
cpp复制#ifndef PROCESSMANAGER_H
#define PROCESSMANAGER_H
#include <QObject>
#include <QVector>
#include <QString>
#include <windows.h>
#include <psapi.h>
struct ProcessInfo {
DWORD pid;
QString name;
QString user;
DWORD memoryUsage;
DWORD priority;
};
class ProcessManager : public QObject
{
Q_OBJECT
public:
explicit ProcessManager(QObject *parent = nullptr);
QVector<ProcessInfo> getProcessList();
bool killProcess(DWORD pid);
bool setPriority(DWORD pid, DWORD priority);
private:
QString getUserNameFromPid(DWORD pid);
QString getProcessNameFromPid(DWORD pid);
DWORD getMemoryUsageFromPid(DWORD pid);
DWORD getPriorityFromPid(DWORD pid);
};
#endif // PROCESSMANAGER_H
3.2 进程列表实现
在processmanager.cpp中实现获取进程列表的具体逻辑:
cpp复制#include "processmanager.h"
#include <tlhelp32.h>
#include <sddl.h>
#include <accctrl.h>
#include <aclapi.h>
ProcessManager::ProcessManager(QObject *parent) : QObject(parent)
{
}
QVector<ProcessInfo> ProcessManager::getProcessList()
{
QVector<ProcessInfo> processList;
// 获取所有进程ID
DWORD processes[1024], cbNeeded;
if(!EnumProcesses(processes, sizeof(processes), &cbNeeded)) {
return processList;
}
// 计算实际进程数量
DWORD processCount = cbNeeded / sizeof(DWORD);
for(DWORD i = 0; i < processCount; i++) {
DWORD pid = processes[i];
if(pid == 0) continue; // 跳过系统空闲进程
ProcessInfo info;
info.pid = pid;
info.name = getProcessNameFromPid(pid);
info.user = getUserNameFromPid(pid);
info.memoryUsage = getMemoryUsageFromPid(pid);
info.priority = getPriorityFromPid(pid);
if(!info.name.isEmpty()) {
processList.append(info);
}
}
return processList;
}
3.3 辅助函数实现
获取进程名、用户名、内存使用量和优先级等辅助函数:
cpp复制QString ProcessManager::getProcessNameFromPid(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if(hProcess == NULL) return QString();
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
if(GetModuleFileNameEx(hProcess, NULL, szProcessName, sizeof(szProcessName)/sizeof(TCHAR))) {
QString name = QString::fromWCharArray(szProcessName);
CloseHandle(hProcess);
return name.mid(name.lastIndexOf('\\') + 1);
}
CloseHandle(hProcess);
return QString();
}
QString ProcessManager::getUserNameFromPid(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if(hProcess == NULL) return QString("SYSTEM");
HANDLE hToken = NULL;
if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
CloseHandle(hProcess);
return QString("SYSTEM");
}
DWORD dwSize = 0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
CloseHandle(hToken);
CloseHandle(hProcess);
return QString("SYSTEM");
}
PTOKEN_USER pTokenUser = (PTOKEN_USER)malloc(dwSize);
if(!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) {
free(pTokenUser);
CloseHandle(hToken);
CloseHandle(hProcess);
return QString("SYSTEM");
}
TCHAR szUser[MAX_PATH] = {0};
TCHAR szDomain[MAX_PATH] = {0};
DWORD cchUser = MAX_PATH, cchDomain = MAX_PATH;
SID_NAME_USE snu;
if(!LookupAccountSid(NULL, pTokenUser->User.Sid, szUser, &cchUser, szDomain, &cchDomain, &snu)) {
free(pTokenUser);
CloseHandle(hToken);
CloseHandle(hProcess);
return QString("SYSTEM");
}
free(pTokenUser);
CloseHandle(hToken);
CloseHandle(hProcess);
return QString::fromWCharArray(szDomain) + "\\" + QString::fromWCharArray(szUser);
}
DWORD ProcessManager::getMemoryUsageFromPid(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if(hProcess == NULL) return 0;
PROCESS_MEMORY_COUNTERS pmc;
if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
CloseHandle(hProcess);
return pmc.WorkingSetSize / 1024; // 返回KB单位
}
CloseHandle(hProcess);
return 0;
}
DWORD ProcessManager::getPriorityFromPid(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if(hProcess == NULL) return 0;
DWORD priority = GetPriorityClass(hProcess);
CloseHandle(hProcess);
return priority;
}
4. 进程控制功能实现
4.1 终止进程实现
终止进程是进程管理器的关键功能之一,我们使用TerminateProcess API实现:
cpp复制bool ProcessManager::killProcess(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if(hProcess == NULL) return false;
bool result = TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
return result;
}
4.2 设置进程优先级
Windows进程可以设置不同的优先级类别,以下是实现代码:
cpp复制bool ProcessManager::setPriority(DWORD pid, DWORD priority)
{
static const QMap<QString, DWORD> priorityMap = {
{"实时", REALTIME_PRIORITY_CLASS},
{"高", HIGH_PRIORITY_CLASS},
{"高于正常", ABOVE_NORMAL_PRIORITY_CLASS},
{"正常", NORMAL_PRIORITY_CLASS},
{"低于正常", BELOW_NORMAL_PRIORITY_CLASS},
{"空闲", IDLE_PRIORITY_CLASS}
};
if(!priorityMap.values().contains(priority)) {
return false;
}
HANDLE hProcess = OpenProcess(PROCESS_SET_INFORMATION, FALSE, pid);
if(hProcess == NULL) return false;
bool result = SetPriorityClass(hProcess, priority);
CloseHandle(hProcess);
return result;
}
5. UI界面设计与实现
5.1 主窗口设计
使用Qt Designer创建主窗口界面,主要包含:
- 一个QTableView用于显示进程列表
- 几个按钮用于刷新、终止进程和设置优先级
- 一个优先级选择下拉框
mainwindow.h内容:
cpp复制#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "processmanager.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onRefreshClicked();
void onKillClicked();
void onSetPriorityClicked();
void onProcessSelected(const QModelIndex &index);
private:
Ui::MainWindow *ui;
ProcessManager m_processManager;
QStandardItemModel *m_model;
DWORD m_selectedPid;
};
#endif // MAINWINDOW_H
5.2 主窗口实现
mainwindow.cpp中的关键实现:
cpp复制#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStandardItemModel>
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_model = new QStandardItemModel(this);
m_model->setHorizontalHeaderLabels({"PID", "进程名", "用户", "内存使用(KB)", "优先级"});
ui->tableView->setModel(m_model);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::onRefreshClicked);
connect(ui->killButton, &QPushButton::clicked, this, &MainWindow::onKillClicked);
connect(ui->setPriorityButton, &QPushButton::clicked, this, &MainWindow::onSetPriorityClicked);
connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::onProcessSelected);
// 初始化优先级下拉框
ui->priorityComboBox->addItems({"实时", "高", "高于正常", "正常", "低于正常", "空闲"});
ui->priorityComboBox->setCurrentText("正常");
onRefreshClicked();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onRefreshClicked()
{
m_model->removeRows(0, m_model->rowCount());
auto processes = m_processManager.getProcessList();
for(const auto &process : processes) {
QList<QStandardItem*> items;
items << new QStandardItem(QString::number(process.pid));
items << new QStandardItem(process.name);
items << new QStandardItem(process.user);
items << new QStandardItem(QString::number(process.memoryUsage));
QString priorityStr;
switch(process.priority) {
case REALTIME_PRIORITY_CLASS: priorityStr = "实时"; break;
case HIGH_PRIORITY_CLASS: priorityStr = "高"; break;
case ABOVE_NORMAL_PRIORITY_CLASS: priorityStr = "高于正常"; break;
case NORMAL_PRIORITY_CLASS: priorityStr = "正常"; break;
case BELOW_NORMAL_PRIORITY_CLASS: priorityStr = "低于正常"; break;
case IDLE_PRIORITY_CLASS: priorityStr = "空闲"; break;
default: priorityStr = "未知";
}
items << new QStandardItem(priorityStr);
m_model->appendRow(items);
}
ui->tableView->resizeColumnsToContents();
}
void MainWindow::onProcessSelected(const QModelIndex &index)
{
if(!index.isValid()) return;
QModelIndex pidIndex = m_model->index(index.row(), 0);
m_selectedPid = m_model->data(pidIndex).toString().toUInt();
}
void MainWindow::onKillClicked()
{
if(m_selectedPid == 0) {
QMessageBox::warning(this, "警告", "请先选择一个进程");
return;
}
if(QMessageBox::question(this, "确认", QString("确定要终止进程 %1 吗?").arg(m_selectedPid)) == QMessageBox::Yes) {
if(m_processManager.killProcess(m_selectedPid)) {
QMessageBox::information(this, "成功", "进程已终止");
onRefreshClicked();
} else {
QMessageBox::warning(this, "失败", "无法终止进程");
}
}
}
void MainWindow::onSetPriorityClicked()
{
if(m_selectedPid == 0) {
QMessageBox::warning(this, "警告", "请先选择一个进程");
return;
}
QString priorityStr = ui->priorityComboBox->currentText();
DWORD priority = 0;
if(priorityStr == "实时") priority = REALTIME_PRIORITY_CLASS;
else if(priorityStr == "高") priority = HIGH_PRIORITY_CLASS;
else if(priorityStr == "高于正常") priority = ABOVE_NORMAL_PRIORITY_CLASS;
else if(priorityStr == "正常") priority = NORMAL_PRIORITY_CLASS;
else if(priorityStr == "低于正常") priority = BELOW_NORMAL_PRIORITY_CLASS;
else if(priorityStr == "空闲") priority = IDLE_PRIORITY_CLASS;
if(m_processManager.setPriority(m_selectedPid, priority)) {
QMessageBox::information(this, "成功", "优先级已设置");
onRefreshClicked();
} else {
QMessageBox::warning(this, "失败", "无法设置优先级");
}
}
6. 常见问题与解决方案
6.1 权限问题处理
在Windows上操作其他进程需要足够的权限,特别是系统进程。以下是几个常见问题及解决方案:
- 无法获取进程信息
- 原因:进程可能运行在更高权限级别
- 解决方案:以管理员身份运行程序
- 代码调整:在main.cpp中添加以下代码请求提升权限
cpp复制#include <windows.h>
#include <shellapi.h>
int main(int argc, char *argv[])
{
// 请求管理员权限
if(IsUserAnAdmin() == FALSE) {
wchar_t szPath[MAX_PATH];
if(GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath))) {
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = L"runas";
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if(!ShellExecuteEx(&sei)) {
QMessageBox::warning(NULL, "错误", "需要管理员权限运行此程序");
return 1;
}
return 0;
}
}
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
- 无法终止某些进程
- 原因:系统关键进程或受保护进程
- 解决方案:检查错误代码,给用户友好提示
- 代码调整:修改killProcess函数
cpp复制bool ProcessManager::killProcess(DWORD pid)
{
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if(hProcess == NULL) {
DWORD err = GetLastError();
if(err == ERROR_ACCESS_DENIED) {
qDebug() << "拒绝访问,可能需要管理员权限";
}
return false;
}
bool result = TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
return result;
}
6.2 性能优化
当系统中有大量进程时,获取进程信息可能会比较慢。以下是几个优化建议:
-
分批次获取进程信息
- 不要一次性获取所有进程的详细信息
- 先获取基本列表,当用户选择某个进程时再获取详细信息
-
缓存进程信息
- 对于不常变化的信息(如进程名、用户)可以缓存
- 定期刷新内存使用量等动态信息
-
使用多线程
- 将耗时的操作(如获取用户名)放到工作线程
- 主线程保持响应
6.3 跨平台考虑
虽然当前实现是针对Windows平台的,但Qt本身是跨平台的。如果要支持其他平台,可以考虑以下架构:
- 抽象接口
cpp复制class IProcessManager {
public:
virtual QVector<ProcessInfo> getProcessList() = 0;
virtual bool killProcess(DWORD pid) = 0;
virtual bool setPriority(DWORD pid, DWORD priority) = 0;
};
- 平台特定实现
cpp复制#ifdef Q_OS_WIN
class WindowsProcessManager : public IProcessManager {
// Windows实现
};
#endif
#ifdef Q_OS_LINUX
class LinuxProcessManager : public IProcessManager {
// Linux实现
};
#endif
- 工厂方法创建实例
cpp复制IProcessManager* createProcessManager()
{
#ifdef Q_OS_WIN
return new WindowsProcessManager();
#elif defined(Q_OS_LINUX)
return new LinuxProcessManager();
#else
return nullptr;
#endif
}
7. 功能扩展建议
这个基础进程管理器还可以进一步扩展以下功能:
-
进程树展示
- 显示进程间的父子关系
- 使用QTreeView替代QTableView
-
性能图表
- 绘制进程CPU和内存使用率的历史曲线
- 使用QChart或第三方库如QCustomPlot
-
进程注入检测
- 检测可疑的DLL注入
- 显示进程加载的模块列表
-
网络连接查看
- 显示进程建立的网络连接
- 使用GetExtendedTcpTable/GetExtendedUdpTable API
-
启动参数查看
- 显示进程的启动命令行参数
- 使用NtQueryInformationProcess等API
-
服务关联
- 显示哪些进程是Windows服务
- 与SCM(服务控制管理器)交互
在实际开发过程中,我发现Windows进程管理API虽然功能强大,但也有很多需要注意的细节。比如在打开进程句柄后必须记得关闭,否则会导致资源泄漏;获取用户名等操作可能会失败,需要做好错误处理;某些API在不同Windows版本上行为可能不同,需要做好兼容性测试。