windows 进程与线程讲解(1)

windows系统

windows系统是C语言编写的,但是它是一个面向对象的操作系统。
Windows一个个的对象,本质上都是结构体变量,windows系统不希望程序员能够直接访问这些结构体变量,所以使用上都有一个特点,都需要先得到他们的句柄(句柄不等于指针)然后再调用相应的API去操作这些对象

Windows对象:
Windows对象大致分为三大类:
USER 对象:窗口、控件、图标、菜单、光标。 user32.dll
GDI对象:画刷、字体、画笔 GDI32.dll
内核对象:文件、进程、线程。Kernel32.dll。

内核对象的创建方式

  1. 创建对象 CreateXXX,我们将在今后慢慢了解这些操作。

CreateProcess: 创建进程
CreateThread: 创建线程
CreateFile: 创建文件
CreatesSemaphore:创建信号量

  1. 打开对象获取句柄:OpenXXX
  2. 通过API操作对象
  3. 关闭句柄 CloseHandle。

内核对象特性

所有的内核对象都属于操作系统内核,可以再不同的进程间访问到,俗称:内核对象是跨进程的。
很多时候,我们都需要在不同的进程中访问同一个内核对象,比如进程间的同步进程间共享数据等等。
通常,我们使用命名的方式在不同进程间使用内核对象。

每一个内核对象都有一个引用计数,也可以说每一个内核对象结构体都有一个字段叫引用计数当有一个进程创建或者打开了此内核对象,那么内核对象的引用计数就会加1;进程终止,或者关闭,引用计数减一,当引用计数为0,则对象销毁。

内核对象都有一个安全描述符,这个描述符说明了谁拥有此对象,哪些用户和组被允许访问或使用此对象,以及哪些用户和组被拒绝访问或使用此对象。
在创建一个内核对象的时候,需要我们传递一个安全描述符

Typedef struct _SECURITY_ATTRIBUTES
{
	DWORD nLength;结构体大小
	LPVOID lpSecurityDescriptor安全描述符
	BOOL bInheritHandle 能否被新创建的进程继承。
}

一般情况下,在创建进内核对象的时候这个安全描述符可以给一个null,这意味着此内核对象将使用与当前令牌相关的默认安全属性。

在windows操作系统中,使用对象就要使用句柄,对于内核对象来说,也是这样。内核对象的句柄是和进程相关的,同一个对象在不同进程中,其句柄值是不同的。这点和GDI对象不同,GDI对象的句柄值,全局有效。由此可见,不同的类型的对象,其管理方式也不同。

句柄表:在每一个进程对象中,都有一个句柄表,用于记录本进程所打开的所有内核对象,可以简单的将句柄表理解为一个一维数组,句柄值可以理解为数组索引。
句柄表中的每一项,描述了使用此句柄访问对象的权限,以及此句柄是否可以被子进程继承。

内核对象的跨进程访问
由于内核对象是操作系统全局的,因此我们可以再多个程序中访问同一个内核对象。
通常有3种方式实现跨进程访问内核对象

  1. 由父进程继承给子进程
  2. 使用名称或者ID作为标识,打开一个内核对象。
  3. 使用DuplicateHandle函数,将一个句柄从一个进程传递给另一个进程。

进程与模块

什么是进程
进程是windows操作系统中的一个重要概念,简单来说运行一个程序就会产生一个进程,进程是操作系统用来分配资源的单位,其中至少包含了一个虚拟的地址空间、地址空间中有加载的exe,同时还有程序运行所有需加载的dll。

什么是模块
包含着代码和数据的可以运行的文件我们称之为可执行文件。
在windows平台常见的可执行文件:
Exe、dll,一个运行中的进程通常都需要将好几个可执行文件加载到他的虚拟内存空间,那么每个在此进程中被加载的可执行文件都称之为模块

进程相关操作

CreateProcess: 创建进程
OpenProcess: 打开进程
ExitProcess: 退出本进程
TerminateProcess: 结束指定进程
WinExec: 启动一个程序
ShellExecute: 运行一个外部程序,或者打开一个文件目录、文件夹等等。
system : 控制台命令
CreateToolhelp32Snapshot :创建快照遍历进程、线程、模块、堆

我们创建一个简单的进程:

#include <Windows.h>

int main()
{
	STARTUPINFOW sw{ 0 };
	PROCESS_INFORMATION pInfo{ 0 };
	BOOL Procerss = CreateProcessW(
		/*要执行的模块名称*/L"F:\\windows编程\\1. 简单的窗口\\Debug\\1. 简单的窗口.exe",
		/*要执行的命令行*/NULL,
		/*可否由子进程继承*/NULL,
		/*可否由子进程继承*/NULL,
		/*是否可被继承*/FALSE,					
		/*进程创建标志*/0,
		/*指向新进程的环境块的指针*/NULL,
		/*指定驱动器与工作目录*/NULL,
		&sw,			//进程启动信息
		&pInfo			//进程运行信息
	);

	CloseHandle(pInfo.hThread);
	CloseHandle(pInfo.hProcess);
	return 0;
}

注意:
最重要的就是第一个和最后两个参数。
需要分别指定子进程的路径和进程启动的信息和运行信息
这里我们运行:弹出了一个提示框。
在这里插入图片描述


获取应用进程

我们要获取某一个应用软件的进程,然后对这个应用软件进行某些操作,如何办到??

  1. 首先获取窗口句柄 Openprocess
  2. 对其进行一些API操作(如关闭它)
int main()
{
	//1. 获得所有正在运行的进程中,ID为35564 的窗口句柄
	HANDLE DrawPic = OpenProcess(PROCESS_ALL_ACCESS, NULL, 43100);
	//2. 关闭此进程
	TerminateProcess(DrawPic, 0);
	return 0;
}

解析:
我们打开 画图板 ,然后打开任务管理器,查看其进程ID:
在这里插入图片描述
我们调用此程序,把这个进程关闭:这个进程就被关闭了
在这里插入图片描述
任何进程,我们只要具有其进程ID,都可以使用TerminateProcess函数将其关闭


遍历所有进程

CreateToolhelp32Snapshot 函数可以遍历所有进程,模块,线程等


int main()
{
	/*
	遍历当前计算机所有进程
	*/
	/*
	第一个参数 :
	TH32CS_SNAPPROCESS 遍历进程
	TH32CS_SNAPTHREAD  遍历线程
	TH32CS_SNAPMODULE  遍历模块
	*/
	setlocale(LC_ALL, "chs");
	HANDLE MainHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,/*遍历进程可以忽略此参*/NULL);
	PROCESSENTRY32W processentry32W{ sizeof(PROCESSENTRY32)};
	BOOL IsSuccess =  Process32FirstW(MainHandle, &processentry32W);
	UINT count = 0;
	if (IsSuccess)
	{
		do
		{
			printf("进程名:%ls,进程ID:%ld\n", processentry32W.szExeFile, processentry32W.th32ProcessID);
			count++;
		} while (Process32NextW(MainHandle, &processentry32W));
	}
	printf("总进程数:%d\n", count);
	CloseHandle(MainHandle);
	return 0;
}

解析:

  1. 使用CreateToolhelp32Snapshot函数获取所有主进程句柄,其中可以设置第一个参数为不同类型,表示接收不同的类型,例如进程和线程,模块。
  2. 使用Process32First和Process32Next配合循环可以遍历正在运行的每一个进程信息。
  3. 使用PROCESSENTRY32W作为结构体,可以保存获取的每一个进程的信息,其中第一个参数必须要设置,它指定了结构体的大小

运行结果:
在这里插入图片描述

遍历所有模块

同上述遍历进程的操作,只不过函数名不一样和参数不一样而已:

int main()
{
	/*
	遍历当前计算机某进程的所有模块
	*/
	setlocale(LC_ALL, "chs");
	//我们要遍历模块,所以我们要指定寻找哪里的模块,即指定一个进程ID,寻找这个进程的所有模块
	HANDLE qqHandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 33864);
	MODULEENTRY32W moduleEntry32W{ sizeof(MODULEENTRY32W) };
	BOOL IsSuccess =  Module32FirstW(qqHandle, &moduleEntry32W);
	UINT count = 0;
	if (IsSuccess)
	{
		do
		{
			printf("模块名:%ls\n", moduleEntry32W.szExePath);
			count++;
		} while (Module32NextW(qqHandle, &moduleEntry32W));
	}
	printf("QQ所拥有的模块数:%d\n", count);
	CloseHandle(qqHandle);
	return 0;
}
  1. CreateToolhelp32Snapshot 的函数变化
  2. Module32First Module32Next 寻找模块

运行如下:
在这里插入图片描述

遍历所有线程

int main()
{
	/*
	遍历QQ所拥有的所有线程
	*/
	setlocale(LC_ALL, "chs");
	HANDLE qqHandle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	THREADENTRY32 threadEntry32{ sizeof(THREADENTRY32) };
	BOOL IsSuccess =  Thread32First(qqHandle, &threadEntry32);
	UINT count = 0;
	if (IsSuccess)
	{
		do
		{
			//注意此处要列举出QQ(ID:33864进程ID相同的)
			if (threadEntry32.th32OwnerProcessID == 33864)
			{
				//打印线程ID
				printf("线程ID:%d\n", threadEntry32.th32ThreadID);
				count++;
			}
		} while (Thread32Next(qqHandle, &threadEntry32));
	}
	printf("QQ所拥有的线程数:%d\n", count);
	CloseHandle(qqHandle);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

可以得知,查找正确。。。

posted @ 2022-10-12 21:46  hugeYlh  阅读(59)  评论(0编辑  收藏  举报  来源