windows进程线程模块操作

1. 进程

进程:运行的程序,可执行文件在内存中的样子。

包含:

  • 虚拟空间
  • exe + dll
  • 进程内核对象
  • 至少一个运行的线程
  • 。。。

创建进程

  • CreateProcess()
  • WinExec()
  • ShellExecute()
  • system()
  • OpenProcess()
  • ExitProcess()
  • TerminateProcess()
#define ExePath L"E:\\Dbgview.exe"

int main()
{

	//窗口属性
	STARTUPINFO si = {};
	si.cb = sizeof(si);
	//进程信息
	PROCESS_INFORMATION pi = {};

	//创建进程
	BOOL bRet = CreateProcess(
		ExePath,  //exe的路径
		NULL,	  //命令行参数
		NULL,	  //进程安全属性
		NULL,	  //线程安全属性
		FALSE,    //是否继承父进程句柄
		NULL,	  //创建标志
		NULL,	  //环境变量
		NULL,	  //当前运行目录
		&si,	  //窗口特性
		&pi
	);
	if (bRet)
	{
		MessageBox(NULL, L"创建成功", NULL, NULL);
	}
	else {
		MessageBox(NULL, L"创建失败", NULL, NULL);
	}

    return 0;

}

结束进程

最常见和最理想的结束方式是,主线程的入口函数返回。

void KillProcess(DWORD dwPid)
{
	//1.获取进程句柄
	HANDLE  hProcess = OpenProcess(
		PROCESS_TERMINATE,		//打开句柄拥有的权限
		FALSE,					//句柄是否能继承
		dwPid					//进程PID
	);
	if (hProcess != INVALID_HANDLE_VALUE)
	{
		//2.结束进程
		TerminateProcess(hProcess, 0);
		//3.关闭句柄
		CloseHandle(hProcess);
	}

}

快照与遍历进程

快照可以用来遍历进程。

快照api包含在<TIHelpp32.h>

步骤:

  1. 创建进程快照
  2. 第一次遍历进程
  3. 循环遍历进程Next
  4. 关闭句柄
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>

void EnumProcess()
{
	//1.创建进程快照
	HANDLE hSnap = CreateToolhelp32Snapshot(
		TH32CS_SNAPPROCESS,			//遍历进程快照1
		0);							//进程PID

	//2.第一次遍历进程
	PROCESSENTRY32  stcPe = {sizeof(stcPe)};
	
	if (Process32First(hSnap, &stcPe))
	{

		//3.循环遍历进程Next
		do {

			wprintf(L"pid :%d   %S\n", stcPe.th32ProcessID, stcPe.szExeFile);
			//获取其他信息
			HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, stcPe.th32ProcessID);
			if (hProcess != INVALID_HANDLE_VALUE)
			{
				GetPriorityClass(hProcess);
				CloseHandle(hProcess);
			}
		} while (Process32Next(hSnap, &stcPe));
	}

	//4.关闭句柄
	CloseHandle(hSnap);
}

int main()
{
	EnumProcess();
    return 0;
}

2. 快照遍历模块

仅32位。

void EnumModule(DWORD dwPid)
{
	//1.创建模块快照
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);


	//2.第一次遍历模块
	MODULEENTRY32 stcMd = { sizeof(stcMd) };
	if (Module32First(hSnap, &stcMd))
	{
		//3.循环遍历模块Next
		do
		{	
			wprintf(L"size:%d path:%S\n", stcMd.dwSize, stcMd.szExePath);
		} while (Module32Next(hSnap, &stcMd));
	}
	CloseHandle(hSnap);

}

遍历模块的正确姿势

BOOL EnumModules96(DWORD dwPid, std::list<MYMODULEINFO>& list)
{
	HANDLE hProcess = OpenProcess(
		PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
		FALSE,
		dwPid);
	HMODULE* hModules = new HMODULE[0x2000]();
	DWORD dwSize = 0, dwModuleCount = 0;

	MYMODULEINFO modinfo;

	if (hProcess == NULL)
	{
		MessageBoxW(NULL, L"EnumModules96() ERROR", L"ERROR", 0);
		return FALSE;
	}
	EnumProcessModulesEx(hProcess, hModules, sizeof(HMODULE) * 0x2000,
		&dwSize, LIST_MODULES_ALL);

	dwModuleCount = dwSize / sizeof(HMODULE);

	size_t i = 0;
	//while(hModules[i])
	for (; i < dwModuleCount; ++i)
	{
		ZeroMemory(modinfo.wszFileName, 0x1000 * sizeof(WCHAR));
		GetModuleFileNameEx(hProcess, hModules[i],
			modinfo.wszFileName, 0x1000);
		GetModuleInformation(hProcess, hModules[i],
			&modinfo.stcModinfo, sizeof(MODULEINFO));
		//wprintf(L"%ls\n", wszFileName);
		//wprintf(L"	EP:			%p\n", (WCHAR*)modinfo.EntryPoint);
		//wprintf(L"	BaseOfDll:	%p\n", (WCHAR*)modinfo.lpBaseOfDll);
		//wprintf(L"	SizeOfImage:%u\n", modinfo.SizeOfImage);
		list.push_back(modinfo);
	}

	delete[] hModules;

	return TRUE;
}

3. 线程

主函数其实也可以配置属性,指入口函数:

#pragma comment(linker, "/entry:\"MyFun\"")

可以打开线程窗口调试。


需要创建线程的情况:

  • 输入的同时也有输出;
  • 一个逻辑依赖于另一个逻辑的实时反馈,如进度条;
  • 复杂但不紧急的任务;
  • 复杂的磁盘操作;
  • 网络程序;
  • 多个需要长时间等待的逻辑;
  • 密集计算;
  • 远程注入。

总结就是:会阻塞当前线程,但当前线程不能被阻塞的情况。

线程是内核对象。

主线程结束,其它线程也结束。

创建线程

#include <windows.h>
#include <stdio.h>

DWORD WINAPI ThreadProc(LPVOID lParam)
{
	int i = 10;
	while (i--)
	{
		wprintf(L"child thread\n");
		Sleep(1000);
	}
	return 0;
}

int main()
{
	HANDLE hThread = CreateThread(NULL,
		NULL,
		ThreadProc,
		NULL,NULL,NULL);
	WaitForSingleObject(
		hThread,
		5000
	);
	int i = 10;
	while (i--)
	{
		wprintf(L"parent thread\n");
		Sleep(1000);
	}
	return 0;
}

遍历线程

#include <windows.h>
#include <stdio.h>
#include<TlHelp32.h>

void EnumThreads(DWORD dwPid)
{
	HANDLE hSnap = INVALID_HANDLE_VALUE;
	THREADENTRY32 te32;

	hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	if (hSnap == INVALID_HANDLE_VALUE)
	{
		return;
	}

	te32.dwSize = sizeof(THREADENTRY32);

	if (Thread32First(hSnap, &te32))
	{
		do
		{
			if (te32.th32OwnerProcessID == dwPid)
			{
				printf("TID:%d\n", te32.th32ThreadID);
			}
		} while (Thread32Next(hSnap,&te32));
	}
	CloseHandle(hSnap);
}

int main()
{
	EnumThreads(GetCurrentProcessId());
	return 0;
}

伪句柄

HANDLE GetCurrentProcess();
HANDLE GetCurrentThread();
UINT GetCurrentProcessiD();
UINT GetCurrentThreadId();

前两个伪句柄仅能在本地使用,不能在其它进程/线程中使用。

每个进程/线程都有自己的伪句柄,指向真正的句柄。



#include <iostream>
#include<Windows.h>

//线程回调函数
DWORD WINAPI ChildThread(PVOID pParam) {

	//观察主线程时间  发现主线程时间不一致,说明这个句柄不是主线程的,它只是伪句柄
	HANDLE hThreadParent = (HANDLE)pParam;
	FILETIME stcCreationTime, stcExitTime;
	FILETIME stcKernelTime, stcUserTime;
	GetThreadTimes(hThreadParent, &stcCreationTime,
		&stcExitTime, &stcKernelTime, &stcUserTime);
	//输出创建时间
	printf("main: h = %d L = %d\n",
		stcCreationTime.dwHighDateTime, stcCreationTime.dwLowDateTime);
	return 0;
}
int main()
{
	//获取当前线程句柄
	HANDLE hThreadParent = GetCurrentThread();
	//显示当前线程时间
	FILETIME stcCreationTime, stcExitTime;
	FILETIME stcKernelTime, stcUserTime;
	GetThreadTimes(hThreadParent, &stcCreationTime,
		&stcExitTime, &stcKernelTime, &stcUserTime);
	//输出创建时间
	printf("main: h = %d L = %d\n",
		stcCreationTime.dwHighDateTime, stcCreationTime.dwLowDateTime);

	//创建线程,将当前主线程句柄传递
	HANDLE hThread = CreateThread(NULL, 0, ChildThread,
		(PVOID)hThreadParent, 0, NULL);
	//等待线程结束
	WaitForSingleObject(hThread, -1);
}



复制句柄


#include <iostream>
#include<Windows.h>

//线程回调函数
DWORD WINAPI ChildThread(PVOID pParam) {

	//观察主线程时间
	HANDLE hThreadParent = (HANDLE)pParam;
	FILETIME stcCreationTime, stcExitTime;
	FILETIME stcKernelTime, stcUserTime;
	GetThreadTimes(hThreadParent, &stcCreationTime,
		&stcExitTime, &stcKernelTime, &stcUserTime);
	//输出创建时间
	printf("main: h = %d L = %d\n",
		stcCreationTime.dwHighDateTime, stcCreationTime.dwLowDateTime);;
	return 0;
}
int main()
{
	//获取当前线程句柄
	HANDLE hThreadParent = GetCurrentThread();
	//复制句柄,变成真句柄
	DuplicateHandle(
		GetCurrentProcess(), // 拥有源句柄的进程句柄
		GetCurrentThread(), // 指定对象的现有句柄(伪句柄)
		GetCurrentProcess(), // 拥有新对象句柄的进程句柄
		&hThreadParent,    // 用于保存新句柄
		0,					// 安全访问级别
		false,				// 是否可以被子进程继承
		DUPLICATE_SAME_ACCESS); // 转换选项
								//显示当前线程时间
	FILETIME stcCreationTime, stcExitTime;
	FILETIME stcKernelTime, stcUserTime;
	GetThreadTimes(hThreadParent, &stcCreationTime,
		&stcExitTime, &stcKernelTime, &stcUserTime);
	//输出创建时间
	printf("main: h = %d L = %d\n",
		stcCreationTime.dwHighDateTime, stcCreationTime.dwLowDateTime);;

	//创建线程,将当前主线程句柄传递
	HANDLE hThread = CreateThread(NULL, 0, ChildThread,
		(PVOID)hThreadParent, 0, NULL);
	//等待线程结束
	WaitForSingleObject(hThread, -1);
}


线程退出

ExitThread()会结束主函数,但不会析构c++对象,因此建议使用_endthreadex()

TerminateThread()只会结束指定线程,也不会析构对象,不建议。

线程睡眠

windows是分时系统,不是实时系统,所以Sleep()有20ms左右的误差。

Sleep(0)则放弃剩余时间片。

线程内核对象

暂停次数:创建线程初始化时,暂停次数为1,不会分配时间片,如果有SUSPEND标志,则初始化为0,

线程环境(context):线程让出cpu后,要保存执行环境,再次获得时间片时,再把环境加载到cpu中。

信号:WaitForSingleObject()会阻塞,线程结束收到信号后才返回。

优先级

32个优先级。

CreateThread()没有提供设置优先级的参数:

windows不允许我们直接操作线程优先级,而是os通过进程优先级与相对线程优先级得出线程优先级,这个数值叫做基本优先级。

操作:

  • CreateProcess()的fdwCreate传入优先级;
  • SetPriorityClass()改变优先级;
  • GetPriorityClass();
  • SetThreadPriority();
  • GetThreadPriority();

动态优先级

线程同步

等待函数

先介绍一下等待函数:WaitForMultipleObjects()WaitForSingleObject()。它们等待可以等待的内核对象,这些对象有激发态和非激发态两个状态。调用后线程会进入等待状态。

特定内核对象变为激发态时,会触发等待函数返回。

返回值:

  • WAIT_ABANDONED:用于互斥体
  • WAIT_OBJECT_0:内核对象变为激发态
  • WAIT_TIMEOUT:超时
  • WAIT_FAILED:失败

当一个对象的状态改变时,称之为成功等待的副作用。

具体同步措施,可以查看我的另一篇博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值