进程操作
常用函数
参考:https://www.bilibili.com/video/BV1By4y1r7Cq
| 函数名称 | 核心功能 | 关键参数 / 说明 | 典型使用场景 | 示例(简化逻辑) |
|---|---|---|---|---|
CreateProcess | 精细创建新进程(含主线程),支持深度控制(环境、权限、窗口状态等) | lpApplicationName(程序路径)、lpCommandLine(命令行参数)、lpStartupInfo(窗口 / 启动配置) | 需深度控制进程启动(如自动化测试、自定义环境变量、重定向输入输出) | CreateProcess(L"notepad.exe", L"test.txt", ...) 启动记事本并打开文件 |
OpenProcess | 打开已有进程,获取进程句柄,为后续操作(终止、读内存等)提供权限支持 | dwDesiredAccess(请求权限,如 PROCESS_TERMINATE)、dwProcessId(目标 PID) | 操作其他进程(调试、强制终止、内存读写) | OpenProcess(PROCESS_TERMINATE, ..., 1234) 获取 PID 为 1234 的进程句柄 |
ExitProcess | 主动退出当前进程,强制终止所有线程,清理进程资源 | uExitCode(进程退出码,供父进程识别状态) | 进程内部主动退出(如控制台程序收尾、GUI 程序响应退出信号) | ExitProcess(0) 正常退出当前进程 |
TerminateProcess | 强制终止指定进程(需先 OpenProcess 拿句柄 ),暴力终止不清理资源 | hProcess(进程句柄,需 PROCESS_TERMINATE 权限)、uExitCode(退出码) | 紧急终止无响应进程、自动化脚本强制关闭程序(需谨慎,可能丢数据) | TerminateProcess(hProc, 0) 终止通过 OpenProcess 获取的进程 |
WinExec | 简易启动程序,功能单一(兼容旧系统遗留 ),可控性弱 | lpCmdLine(命令行,如 "notepad.exe test.txt")、uCmdShow(窗口显示方式,如 SW_NORMAL) | 简单启动程序,无需复杂控制(微软推荐优先用 CreateProcess ) | WinExec("notepad.exe test.txt", SW_NORMAL) 启动记事本打开文件 |
ShellExecute | 调用系统 Shell 执行操作,支持 “打开 / 编辑 / 打印” 文件、访问网址等系统关联行为 | hwnd(父窗口句柄,可为 NULL)、lpOperation(操作类型,如 "open")、lpFile(目标路径) | 需调用系统默认行为(如双击文件、打开网页),如软件内 “打开日志”“访问官网” | ShellExecute(NULL, L"open", L"https://baidu.com", ...) 浏览器打开百度 |
system | 调用控制台命令(依赖系统 cmd.exe ),跨平台性好(C 标准库函数 ) | command(命令字符串,如 "notepad.exe"、"dir") | 简单执行控制台命令,或跨平台代码中启动程序 / 执行命令 | system("notepad.exe") 启动记事本(Windows 下);system("ls") 查看目录(Linux) |
CreateToolhelp32Snapshot | 创建系统 “快照”,用于枚举进程、线程、模块、堆等信息 | dwFlags(快照类型,如 TH32CS_SNAPPROCESS 枚举进程)、th32ProcessID(目标 PID,0 枚举全部) | 开发 “进程管理器” 类工具,遍历系统进程、查模块、分析线程等 | CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 枚举所有进程 |
进程操作实例——遍历进程
CreateToolhelp32Snapshot函数用于创建系统快照,为什么要创建快照?因为进程随时可能发生变化,需要先创建快照固定一下。
Process32FirstW 是 Windows API 中的一个函数,用于从进程快照中获取第一个进程的信息。它是 Tool Help Library 的一部分,与 CreateToolhelp32Snapshot、Process32NextW 等函数配合使用,实现系统进程的枚举:
BOOL Process32FirstW(
HANDLE hSnapshot, // 进程快照句柄(由 CreateToolhelp32Snapshot 返回)
LPPROCESSENTRY32W lppe // 指向 PROCESSENTRY32W 结构体的指针,用于存储进程信息
);
PROCESSENTRY32W结构体:
typedef struct tagPROCESSENTRY32W {
DWORD dwSize; // 结构体大小(必须先赋值!)
DWORD cntUsage; // 进程引用计数
DWORD th32ProcessID; // 进程 ID(PID)
ULONG_PTR th32DefaultHeapID; // 进程默认堆的 ID
DWORD th32ModuleID; // 进程模块 ID
DWORD cntThreads; // 进程创建的线程数
DWORD th32ParentProcessID;// 父进程 ID
LONG pcPriClassBase; // 线程优先级
DWORD dwFlags; // 保留字段
WCHAR szExeFile[MAX_PATH];// 进程可执行文件路径(宽字符)
} PROCESSENTRY32W;
以下代码可以实现遍历进程:
#include<Windows.h> // Windows API 核心头文件,提供进程、线程、内存等操作接口
#include<TlHelp32.h> // 进程/线程/模块快照工具,用于枚举系统进程
int main()
{
// 设置控制台区域语言为中文(注意:需配合控制台编码设置才能正常显示中文)
setlocale(LC_ALL, "chs");
// ============== 进程枚举功能 ==============
// 创建系统进程快照(TH32CS_SNAPPROCESS 表示获取所有进程的快照)
// 第二个参数 0 表示针对所有进程(若填 PID 则针对单个进程)
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// 初始化进程信息结构体(必须设置 dwSize 字段,否则 Process32FirstW 会失败)
PROCESSENTRY32W processEntry = { sizeof(PROCESSENTRY32W) };
// 获取第一个进程信息(返回值为 BOOL 类型,指示是否成功),
BOOL bSuccess = Process32FirstW(hSnapshot, &processEntry);
// 循环遍历所有进程
if (bSuccess) {
do {
// 输出进程信息:
// - processEntry.th32ProcessID:进程 ID
// - processEntry.szExeFile:进程可执行文件名(宽字符字符串)
// 注意:%ls 用于格式化宽字符字符串,但需确保控制台支持 Unicode 输出
printf("\n进程id:%d,进程名称:%ls",
processEntry.th32ProcessID,
processEntry.szExeFile);
} while (Process32NextW(hSnapshot, &processEntry)); // 获取下一个进程
}
CloseHandle(hSnapshot);
return 0;
}
效果:

同样的,通过下述代码可以遍历某进程的模块:
#include<Windows.h>
#include<TlHelp32.h>
#include<iostream>
int main()
{
// 设置本地语言环境为中文(确保控制台能正确显示中文字符)
setlocale(LC_ALL, "chs");
// 创建 PID 为 5688 的进程的模块快照(用于枚举该进程加载的 DLL)
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 5688);
// 初始化模块信息结构体(必须设置 dwSize 字段)
MODULEENTRY32W moduleEntry = { sizeof(MODULEENTRY32W) };
// 获取第一个模块信息
BOOL bSuccess = Module32FirstW(hSnapshot, &moduleEntry);
if (bSuccess) {
do {
// 输出模块路径(注意:使用 %ls 格式化宽字符字符串)
printf("\n模块名称:%ls", moduleEntry.szExePath);
} while (Module32NextW(hSnapshot, &moduleEntry));
}
}
效果:

进程间通信
| 通信方式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 管道(Pipe) | 内核中开辟的共享缓冲区,分为匿名管道(父子进程)和命名管道(任意进程) | 实现简单,数据按先进先出顺序传输,有内置同步机制 | 匿名管道仅支持父子进程,吞吐量较低,不支持双向同时通信 | 简单数据传输,如命令行程序输出重定向 |
| 共享内存(Shared Memory) | 多个进程映射同一块物理内存,直接读写数据 | 速度最快的 IPC 方式,适合大量数据传输,同步需写代码实现 | 需要手动实现同步机制(如互斥量),权限配置不当易引发访问错误(如错误码 5) | 大数据量实时交换(如图像、视频数据) |
| 消息队列(Message Queue) | 内核维护的消息链表,进程可发送 / 接收带类型的消息包 | 支持消息类型分类,异步通信,解耦进程间依赖 | 存在消息拷贝开销,性能低于共享内存 | 异步通知、任务调度 |
| 信号量(Semaphore) | 计数器机制,控制对共享资源的访问权限 | 轻量级同步工具,可控制多进程对资源的并发访问 | 仅用于同步控制,不直接传输数据 | 资源访问控制(如文件、数据库连接) |
| 互斥量(Mutex) | 独占式锁,确保同一时间只有一个进程访问资源 | 实现简单,自动处理线程所有权,避免死锁(部分实现) | 性能开销高于信号量,可能产生饥饿现象 | 保护临界区,如共享变量访问 |
| 命名事件(Named Event) | 内核对象,通过状态(触发 / 未触发)通知进程事件发生 | 支持跨进程同步,可设置自动 / 手动重置模式 | 仅用于同步通知,不传输数据 | 进程间状态同步(如任务完成通知) |
| 套接字(Socket) | 网络编程接口,支持本地(IPC)和网络通信 | 跨平台性好,支持远程进程通信,接口统一 | 本地通信性能略低于专用 IPC 方式 | 跨主机通信或本地复杂通信架构 |
共享内存
Sender端通过CreateFileMapping创建共享内存,然后用MapViewOfFile映射到当前内存空间即可使用该内存,往里写入信息。Receiver端打开这块共享内存,用MapViewOfFile映射到当前内存空间即可使用该内存,读出相关信息。
Sender:
#include <windows.h>
#include <iostream>
#include <string>
#define SHARED_MEM_NAME L"Local\\MyIPCSharedMemory" // 使用 Local 命名空间
#define BUFFER_SIZE 1024
int main() {
// 创建共享内存对象(使用默认安全属性,避免权限问题)
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 使用系统分页文件
NULL, // 默认安全属性(关键修改:不使用 Global 前缀)
PAGE_READWRITE, // 读写权限
0, // 高位文件大小
BUFFER_SIZE, // 共享内存大小
SHARED_MEM_NAME // 本地命名空间名称
);
if (hMapFile == NULL) {
std::cerr << "创建共享内存失败,错误码: " << GetLastError() << std::endl;
return 1;
}
// 将共享内存映射到当前进程的地址空间
LPVOID pBuffer = MapViewOfFile(
hMapFile, // 共享内存句柄
FILE_MAP_ALL_ACCESS, // 读写访问
0, // 偏移量(高位)
0, // 偏移量(低位)
BUFFER_SIZE // 映射大小
);
if (pBuffer == NULL) {
std::cerr << "映射共享内存失败,错误码: " << GetLastError() << std::endl;
CloseHandle(hMapFile);
return 1;
}
// 向共享内存写入数据
std::string message = "Hello from Sender! 当前时间: " +
std::to_string(GetTickCount());
memcpy(pBuffer, message.c_str(), message.size() + 1); // +1 包含字符串结束符
std::cout << "已往共享内存中写入: " << message << std::endl;
// 等待用户确认后清理资源
std::cout << "按Enter键退出..." << std::endl;
std::cin.get();
// 解除映射并关闭句柄
UnmapViewOfFile(pBuffer);
CloseHandle(hMapFile);
return 0;
}
Receiver:
#include <windows.h>
#include <iostream>
#define SHARED_MEM_NAME L"Local\\MyIPCSharedMemory" // 与发送端保持一致
#define BUFFER_SIZE 1024
int main() {
// 打开已存在的共享内存对象
HANDLE hMapFile = OpenFileMapping(
FILE_MAP_READ, // 只读访问
FALSE, // 不继承句柄
SHARED_MEM_NAME // 共享内存名称
);
if (hMapFile == NULL) {
std::cerr << "打开共享内存失败,错误码: " << GetLastError() << std::endl;
std::cerr << "请先运行发送端程序!" << std::endl;
return 1;
}
// 将共享内存映射到当前进程的地址空间
LPVOID pBuffer = MapViewOfFile(
hMapFile, // 共享内存句柄
FILE_MAP_READ, // 只读访问
0, // 偏移量(高位)
0, // 偏移量(低位)
BUFFER_SIZE // 映射大小
);
if (pBuffer == NULL) {
std::cerr << "映射共享内存失败,错误码: " << GetLastError() << std::endl;
CloseHandle(hMapFile);
return 1;
}
// 从共享内存读取数据并输出
std::cout << "从共享内存读取数据: " << static_cast<const char*>(pBuffer) << std::endl;
// 等待用户确认后清理资源
std::cout << "按Enter键退出..." << std::endl;
std::cin.get();
// 解除映射并关闭句柄
UnmapViewOfFile(pBuffer);
CloseHandle(hMapFile);
return 0;
}
效果:

管道
服务器端先使用CreateNamedPipe创建命名管道,然后使用ConnectNamedPipe来阻塞等待客户端连接,客户端使用CreateFile连接管道,连接成功后服务器端使用WriteFile写入数据,然后客户端使用ReadFile来读取管道信息。(其实这里设计上存在条件竞争问题)
服务器端:
#include <windows.h> // Windows API头文件
#include <iostream> // 标准输入输出流
#include <string> // 字符串处理
#define PIPE_NAME L"\\\\.\\pipe\\MyNamedPipe" // 管道名称(必须以"\\\\.\\pipe\\"开头)
int main() {
// 创建命名管道(服务器端)
// 参数说明:
// - PIPE_ACCESS_OUTBOUND:管道只用于输出(发送数据)
// - PIPE_TYPE_MESSAGE:消息类型管道(保持消息边界)
// - PIPE_READMODE_MESSAGE:读取模式为消息
// - PIPE_WAIT:阻塞模式(等待客户端连接)
HANDLE hPipe = CreateNamedPipe(
PIPE_NAME, // 管道名称
PIPE_ACCESS_OUTBOUND, // 管道访问模式(输出)
PIPE_TYPE_MESSAGE | // 消息类型管道
PIPE_READMODE_MESSAGE | // 消息读取模式
PIPE_WAIT, // 阻塞模式
1, // 最大实例数
0, // 输出缓冲区大小
0, // 输入缓冲区大小
0, // 默认超时值
NULL // 默认安全属性
);
if (hPipe == INVALID_HANDLE_VALUE) {
std::cerr << "创建管道失败,错误码: " << GetLastError() << std::endl;
return 1;
}
std::cout << "等待客户端连接..." << std::endl;
// 等待客户端连接(阻塞调用)
if (ConnectNamedPipe(hPipe, NULL) != FALSE) {
// 准备要发送的消息(包含时间戳)
std::string message = "Hello from Named Pipe! 当前时间: " +
std::to_string(GetTickCount());
DWORD bytesWritten;
// 向管道写入数据
if (WriteFile(hPipe, message.c_str(), message.size(), &bytesWritten, NULL)) {
std::cout << "已发送: " << message << std::endl;
}
}
CloseHandle(hPipe); // 关闭管道句柄
return 0;
}
客户端:
#include <windows.h>
#include <iostream>
#define PIPE_NAME L"\\\\.\\pipe\\MyNamedPipe"
int main() {
// 连接到命名管道(客户端)
// 参数说明:
// - GENERIC_READ:以只读模式打开管道
// - OPEN_EXISTING:打开已存在的管道
HANDLE hPipe = CreateFile(
PIPE_NAME, // 管道名称
GENERIC_READ, // 只读访问
0, // 不共享
NULL, // 默认安全属性
OPEN_EXISTING, // 打开已存在的管道
0, // 默认属性
NULL // 不使用模板文件
);
if (hPipe == INVALID_HANDLE_VALUE) {
std::cerr << "连接管道失败,错误码: " << GetLastError() << std::endl;
return 1;
}
char buffer[1024]; // 读取缓冲区
DWORD bytesRead; // 实际读取的字节数
// 从管道读取数据(阻塞调用)
if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) {
buffer[bytesRead] = '\0'; // 添加字符串结束符
std::cout << "接收到: " << buffer << std::endl;
}
CloseHandle(hPipe); // 关闭管道句柄
return 0;
}
效果:

消息队列
接收端CreateMailslot创建消息队列,使用ReadFile的阻塞模式来等待发送端连接,发送端使用CreateFile来连接消息队列,然后使用WriteFile来往消息队列里面写入信息,接收端的ReadFile成功接收信息。
发送端:
#include <windows.h>
#include <iostream>
#include <string>
#define MAILSLOT_NAME L"\\\\.\\mailslot\\ClientQueue" // 必须与客户端一致
int main() {
std::cout << "服务器启动,连接客户端队列..." << std::endl;
// 服务器端连接客户端创建的队列(发送端)
HANDLE hMailslot = CreateFile(
MAILSLOT_NAME, // 客户端创建的队列名称
GENERIC_WRITE, // 只写权限
FILE_SHARE_READ, // 共享读取
NULL,
OPEN_EXISTING, // 打开已存在的队列
0,
NULL
);
if (hMailslot == INVALID_HANDLE_VALUE) {
std::cerr << "连接队列失败,错误码: " << GetLastError() << std::endl;
return 1;
}
std::cout << "已连接到客户端队列,准备发送消息..." << std::endl;
std::string message = "来自服务器的消息:这是客户端接收、服务器发送的测试";
DWORD bytesWritten = 0;
// 发送消息
if (WriteFile(hMailslot, message.c_str(), message.size(), &bytesWritten, NULL)) {
std::cout << "服务器已发送消息: " << message << std::endl;
}
else {
std::cerr << "发送消息失败,错误码: " << GetLastError() << std::endl;
}
CloseHandle(hMailslot);
return 0;
}
接收端:
#include <windows.h>
#include <iostream>
#include <string>
#define MAILSLOT_NAME L"\\\\.\\mailslot\\ClientQueue" // 客户端创建的队列名称
#define BUFFER_SIZE 1024
int main() {
std::cout << "客户端启动,创建消息队列等待服务器消息..." << std::endl;
// 客户端创建消息队列(接收端)
HANDLE hMailslot = CreateMailslot(
MAILSLOT_NAME, // 队列名称
0, // 消息最大长度
MAILSLOT_WAIT_FOREVER, // 无限等待
NULL
);
if (hMailslot == INVALID_HANDLE_VALUE) {
std::cerr << "创建队列失败,错误码: " << GetLastError() << std::endl;
return 1;
}
std::cout << "队列创建成功,等待服务器发送消息..." << std::endl;
char buffer[BUFFER_SIZE] = { 0 };
DWORD bytesRead = 0;
// 读取消息(阻塞模式)
if (ReadFile(hMailslot, buffer, BUFFER_SIZE - 1, &bytesRead, NULL)) {
buffer[bytesRead] = '\0';
std::cout << "客户端接收到消息: " << buffer << std::endl;
}
else {
std::cerr << "读取消息失败,错误码: " << GetLastError() << std::endl;
}
CloseHandle(hMailslot);
return 0;
}
效果:

从上述几个示例可以看出,这几个通信方式其实逻辑上大同小异,只不过使用的函数和底层的实现原理有区别。