4 进程控制

4.1 获得系统进程

使用toolhelp模块可以实现获取系统中当前运行当中的进程列表。

思路如下,使用CreateToolhelp32Snapshot函数给当前系统内执行的进程拍快照(Snapshot),也就是获得了进程列表,这个列表记录着进程的ID、进程对应的可执行文件的名称和创建该进程的进程ID等数据。然后使用Process32First函数和Process32Next函数遍历快照中记录的列表。

#include <windows.h>
#include <TlHelp32.h> // for snapshot
#include <stdio.h> int main(int argc, char* argv[])
{
PROCESSENTRY32 pe32;
int iProcessCount; // initial its size
pe32.dwSize = sizeof(pe32);
iProcessCount = 0; // create snapshot for all processes
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot failure!\n");
exit(1);
} // traversal snapshot
BOOL bMore = ::Process32First(hProcessSnap, &pe32);
while (bMore)
{
printf("%-20.20s%6u%4u%6u%6u%6u%3ld\n", pe32.szExeFile,
pe32.th32ProcessID, pe32.cntUsage, pe32.th32DefaultHeapID,
pe32.th32ModuleID, pe32.th32ParentProcessID, pe32.pcPriClassBase);
bMore = ::Process32Next(hProcessSnap, &pe32);
iProcessCount ++;
} // clear snapshot
::CloseHandle(hProcessSnap); printf("Processes count: %d\n\n", iProcessCount); getchar();
return 0;
}

结果很容易理解。

CreateToolhelp32Snapshot用于获取系统内指定进程的快照,也可以获取这些进程使用的堆、模块、线程的快照。如:

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, // 指定快照内容
DWORD th32ProcessID // 指定进程ID
);

dwFlags参数可以是如下值:

  • TH32CS_SNAPHEAPLIST枚举`th32ProcessID参数指定进程中的堆。
  • TH32CS_SNAPMODULE枚举`th32ProcessID参数指定进程中的模块。
  • TH32CS_SNAPPROCESS,此时`th32ProcessID参数被忽略。
  • TH32CS_SNAPTHREAD,此时`th32ProcessID参数被忽略。
  • TH32CS_SNAPALL,相当于以上4个值的并集。

根据获取快照的不同,使用下面几组函数来获取快照信息:

  • Heap32ListFirst Heap32ListNext
  • Module32First Module32Next
  • Process32First Process32Next
  • Thread32First Thread32Next
  • Heap32First Heap32Next

以上函数的第二个参数是指向如下之一结构的指针:

  • HEAPLIST32
  • HEAPENTRY32
  • MODULEENTRY32
  • THREADENTRY32
  • PROCESSENTRY32
typedef struct tagHEAPLIST32 {
DWORD dwSize;
DWORD th32ProcessID;
DWORD th32HeapID;
DWORD dwFlags;
} HEAPLIST32; typedef struct tagHEAPENTRY32
{
DWORD dwSize;
HANDLE hHandle;
DWORD dwAddress;
DWORD dwBlockSize;
DWORD dwFlags;
DWORD dwLockCount;
DWORD dwResvd;
DWORD th32ProcessID;
DWORD th32HeapID;
} HEAPENTRY32; typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
TCHAR szModule[MAX_MODULE_NAME32 + 1];
TCHAR szExePath[MAX_PATH];
DWORD dwFlags
} MODULEENTRY32; typedef struct tagTHREADENTRY32{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
DWORD th32AccessKey;
DWORD th32CurrentProcessID;
} THREADENTRY32; typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
DWORD th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
DWORD th32MemoryBase;
DWORD th32AccessKey;
} PROCESSENTRY32;

4.2 终止当前进程

终止一个进程有如下4种方式:

  1. 主线程的入口函数返回。
  2. 进程中一个线程调用了ExitProcess函数。
  3. 此进程中的所有线程都结束了。
  4. 其他进程中的一个线程调用了TerminateProcess函数。

最常用的方式是让主线程的入口函数返回。当入口函数返回时,启动函数会调用C/C++运行期退出函数exit,并将用户的返回值传递给它。exit函数会销毁所有的全局的或者静态的C++对象,然后调用系统函数ExitProcess使操作系统终止应用程序。ExitProcess是一个API函数,它会结束当前应用程序的执行,并设置它的退出代码。

VOID ExitProcess(
UINT uExitCode // 所有线程的退出代码
);

在程序的任何地方都可以调用ExitProcess,强制当前程序的执行立即结束。这对操作系统来说,是正常的。但对C/C++应用程序应该避免直接调用这个函数。因为这会使C/C++运行期库得不到通知,而没有机会去调用全局的或者静态的C++对象的析构函数。

4.3 终止其他进程

ExitProcess函数只能用来结束当前进程,不能用于结束其他进程。如果要终止其他进程,可以使用TerminateProcess函数。

BOOL TerminateProcess(
HANDLE hProcess, // 目标进程句柄
UINT uExitCode // 目标进程的退出代码
);

前面介绍过,使用CreateProcess函数创建新的进程,会得到新进程的句柄。对于不是自己创建的进程,可以使用OpenProcess函数来取得这进程的访问权限,函数如下:

HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问权限
BOOL bInheritHandle, // 返回的句柄是否可被继承
DWORD dwProcessId // 要打开的进程的ID
);

参数dwDesiredAccess指定的对进程的访问权限,可以是如下值:

说明
PROCESS_ALL_ACCESS 所有可进行的权限
PROCESS_CREATE_PROCESS 创建一个进程
PROCESS_CREATE_THREAD 创建一个线程
PROCESS_DUP_HANDLE 做为DuplicateHandle参数
PROCESS_QUERY_INFORMATION 查看进程信息的权限,如优先级类等
PROCESS_QUERY_LIMITED_INFORMATION 需要返回特定信息
PROCESS_SET_INFORMATION 设置进程的优先级
PROCESS_SET_QUOTA 设置内存限制
PROCESS_SUSPEND_RESUME 睡眠和唤醒
PROCESS_TERMINATE 关闭进程
PROCESS_VM_OPERATION 修改进程的地址空间
PROCESS_VM_READ 读进程内存
PROCESS_VM_WRITE 写进程内存
SYNCHRONIZE 等待进程结束

在进程结束后,调用GetExitCodeProcess函数可以取得其退出代码,如果调用时,目标进程还没有结束,此函数会返回STILL_ACTIVE,表示进程还在运行。通过该函数可以检测进程是否结束了。

BOOL GetExitCodeProcess(
HANDLE hProcess, // 目标进程句柄
LPDWORD lpExitCode // 目标进程的退出句柄
);

如果进程已经结束,其退出代码是通过下面几种方式设置的:

  1. ExitProcess或者TerminateProcess函数中指定。
  2. main或者WinMain的返回值。
  3. 由于未处理的异常导致的结束,返回相应的异常值。

一旦进程终止,就会如下事件发生:

  1. 所有被这个进程创建的或撕开的对象句柄就会关闭。
  2. 此进程内的所有线程将终止执行。
  3. 进程内核对象变成受信状态,所有等待在此对象上的线程开始运行,即WaitForSingleObject函数返回。
  4. 系统将进程对象中退出代码值由STILL_ACTIVE设置为指定的退出代码。

Windows程序设计(1)——Win32运行原理(三)的更多相关文章

  1. Windows程序设计(1)——Win32运行原理(一)

    CPU保护模式与Windows系统 1 Windows多任务 2 虚拟内存 3 处理器的特权级别 内核对象 1 内核对象有什么用 2 对象句柄 3 使用计数 1 CPU保护模式与Windows系统 8 ...

  2. Windows程序设计(1)——Win32运行原理(二)

    创建进程 1 进程和线程 2 应用程序的启动过程 3 CreateProcess函数 4 实例 3 创建进程 3.1 进程和线程 进程通常被定义为一个存在运行的程序的实例.进程是一个正在运行的程序,它 ...

  3. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

  4. C++学习笔记1(Windows程序运行原理及程序编写流程)

    窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与Lessonecl调用规范的比较,初学者常犯错误及注意事项.以下是应用程序与操作 ...

  5. 初级游戏外挂编程详解 windows运行原理+游戏辅助编程 游戏外挂编程

    详解游戏辅助编程 [目录] 1-什么是Windows API 2-Windows进程 3-Windows 的内存的运行原理 4-windows 中句柄的概念 5-Windows的变量类型 6-辅助实现 ...

  6. 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制

    [原创]分布式之数据库和缓存双写一致性方案解析(三)   正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...

  7. RocketMQ详解(三)启动运行原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  8. Windows 程序设计

    一.Win32 API /******************************************************************** created: 2014/04/1 ...

  9. Windows程序设计(0)——编程之前

    Windows程序设计之前 1 做什么 2 解决什么问题 3 有哪些资源 在开始真正的编程之前,需要了解要做的事情是什么,要解决的解决的问题是什么,有哪些资源可以使用. 1 Windows程序设计之前 ...

随机推荐

  1. 饼系列—圈饼 doughnut

    chartControl属性 ApplicationSettings: Name:控件名字 AccessibleDescription: ??? AccessibleName:??? Accessib ...

  2. liteos事件(六)

    1. 概述 1.1 基本概念 事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输.一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事 ...

  3. [Inside HotSpot] Java分代堆

    [Inside HotSpot] Java分代堆 1. 宇宙初始化 JVM在启动的时候会初始化各种结构,比如模板解释器,类加载器,当然也包括这篇文章的主题,Java堆.在hotspot源码结构中gc/ ...

  4. Android-TextView属性ellipsize多行失效的解决思路

    多余文字显示省略号的常规做法 android:ellipsize="end" //省略号显示在末尾 android:ellipsize="middle" //省 ...

  5. java高级编程-使用反射强制给private字段赋值

    转自:http://blog.csdn.net/yaerfeng/article/details/7103397 今天项目中遇到了一个问题,要调用一个类,并获取这个类的属性进行赋值然后将这个类传递到方 ...

  6. python matplotlib包图像配色方案

    可选的配色方案: Accent, Accent_r, Blues, Blues_r, BrBG, BrBG_r, BuGn, BuGn_r, BuPu, BuPu_r, CMRmap, CMRmap_ ...

  7. [C++11]_[0基础]_[左值引用声明和右值引用声明]

    场景: 在 remove_reference 结构体中能看到右值引用的身影 &&, 那么这里的右值引用究竟有什么用呢? 常常也发现int& 和int&& 这两种 ...

  8. angular - 配置package.json -3

    package.json 包含了所有的开发包以及全局包以及其它项目信息,我们这个项目需要用到 bootstrap,所以我们添加信息. 添加包信息以后,我们用 npm install 安装,npm包管理 ...

  9. Android自己定义控件

    今天我们来讲一下 Android中自己定义控件的介绍,在Android中, 我们一般写xml都是用的是单个的控件来完毕的 ,但是.往往在一些项目中.单个控件有时是满足不了的.故此我们能够自己定义控件 ...

  10. Lead软件项目半年感受

    Lead一个项目快半年了,整体来说是个辛苦活. 除了自己的研发进度,还要负责对上,对下,对外的交流.这里记录一些感受.     对上的交流,除了确保正确理解老大的安排.就是确保老大在和他的lead以及 ...