Win32编程day14 学习笔记
一 Windows进程
1 Windows进程
进程是一个容器,包含了一个应用程序实例的各种资源。Windows多任务的操作系统,因此可以同时执行多个进程。
2 Windows进程的一些特点
2.1 进程中包含了执行代码等资源。
2.2 进程都具有私有的地址空间。
2.3 每个进程都有一个ID,标识进程。
2.4 每个进程都有自己的安全属性
2.5 至少要包含一个可以执行的线程。
二 进程的环境
1 环境信息的获取
获取:
LPVOID GetEnvironmentStrings(VOID)
返回值是获取到的所有环境信息
释放:
BOOL FreeEnvironmentStrings(
LPTSTR lpszEnvironmentBlock )
2 环境变量的获取和设置
获取:
DWORD GetEnvironmentVariable( LPCTSTR lpName, //变量名称 LPTSTR lpBuffer, //数据BUFF DWORD nSize //BUFF的长度 );
返回值是获取到的字符串的长度
设置:
BOOL SetEnvironmentVariable( LPCTSTR lpName, //变量名称 LPCTSTR lpValue //变量的值 );
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// ProcInfo.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" void EnvVariable( LPSTR pszVar ) { //设置指定变量的值 SetEnvironmentVariable( pszVar, "C:\\" ); //获取指定的变量的值 CHAR szValue[1024] = { 0 }; GetEnvironmentVariable( pszVar, szValue, 1024 ); printf( "%s: %s\n", pszVar, szValue ); } void EnvString( ) { //获取所有环境信息 LPSTR pszEnv = ( LPSTR ) GetEnvironmentStrings( ); LPSTR pszTmp = pszEnv; while( 0 != pszTmp[0] ) { printf( "%s\n", pszTmp ); pszTmp = strlen(pszTmp) + 1 + pszTmp; } //释放环境信息字符串 FreeEnvironmentStrings( pszEnv ); } int main(int argc, char* argv[]) { EnvString( ); EnvVariable( "MYPATH" ); return 0; }
三 进程的信息
1 进程ID和句柄
GetCurrentProcessID 获取进程的ID
GetCurrentProcess 获取进程的句柄,
返回值为-1,是当前进程的伪句柄,永远是-1.如果想获取当前进程的实际句柄需要使用OpenProcess函数.
2 打开进程
HANDLE OpenProcess( DWORD dwDesiredAccess, //访问模式 BOOL bInheritHandle, //继承标识 DWORD dwProcessId //进程ID );
返回进程的句柄
3 获取进程的所使用的所有模块(EXE或DLL)
使用PSAPI函数.
BOOL EnumProcessModules( HANDLE hProcess,//进程句柄 HMODULE * lphModule,//模块的数组 DWORD cb, //数组的长度 LPDWORD lpcbNeeded //获取到数据的字节数 );
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// ProcBase.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" #include "../psapi/psapi.h" #pragma comment( lib, "../psapi/psapi.lib" ) void ProcModule( ) { printf( "All Modules\n" ); //当前进程句柄 HANDLE hProc = GetCurrentProcess( ); //获取模块句柄 HMODULE hModules[256] = { 0 }; DWORD nNeed = 0; EnumProcessModules( hProc, hModules, 256, &nNeed ); //计算获取到句柄数量 DWORD nCount = nNeed / sizeof(HMODULE); for( DWORD nIndex=0; nIndex<nCount; nIndex++ ) { //获取模块的文件名及路径 CHAR szPath[MAX_PATH] = { 0 }; GetModuleFileNameEx( hProc, hModules[nIndex], szPath, MAX_PATH ); //打印 printf( "\t%d: %p, %s\n", nIndex+1, hModules[nIndex], szPath ); } } void ProcInfo( ) { //获取进程ID DWORD nID = GetCurrentProcessId( ); //获取进程句柄(-1,伪句柄) HANDLE hProc = GetCurrentProcess( ); printf( "Process ID: %d\n", nID ); printf( "Process Handle: %p\n", hProc ); //根据进程ID获取进程实际句柄 hProc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, nID ); printf( "Process Handle: %p\n", hProc ); } int main(int argc, char* argv[]) { ProcInfo( ); ProcModule( ); return 0; }
四 进程的使用
1 创建进程
WinExec 16位,现在不使用
ShellExecute 执行打开文件等操作.
CreateProcess 执行一个EXE可执行文件.创建一个进程以及它的主线程.
BOOL CreateProcess( LPCTSTR lpApplicationName,//应用程序路径名 LPTSTR lpCommandLine, //命令行 LPSECURITY_ATTRIBUTES lpProcessAttributes, //进程安全属性 LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性 BOOL bInheritHandles, //句柄继承标识 DWORD dwCreationFlags, //创建标识 LPVOID lpEnvironment, //环境块 LPCTSTR lpCurrentDirectory,//当前目录 LPSTARTUPINFO lpStartupInfo,//启动参数 LPPROCESS_INFORMATION lpProcessInformation //进程信息 );
当进程创建成功,可以从进程信息中获取创建好的进程句柄\ID等.
如果执行程序是16的程序,那么只能使用lpCommandLine设置执行程序路径.
2 打开进程
OpenProcess
3 结束进程
VOID ExitProcess( UINT uExitCode ); (用得较少)
BOOL TerminateProcess( HANDLE hProcess, //进程句柄 UINT uExitCode ); //结束代码
4 等候进程结束
DWORD WaitForSingleObject( HANDLE hHandle, //等候的句柄 DWORD dwMilliseconds );//等候的时间,毫秒
阻塞函数,当运行时,会在等候的时间的时间内,
等待句柄的信号.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// ProcUse.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "conio.h" #include "windows.h" void Create( ) { STARTUPINFO si = { 0 }; PROCESS_INFORMATION pi = { 0 }; si.cb = sizeof( si ); si.dwFlags = STARTF_USESIZE; si.dwXSize = 100; si.dwYSize = 200; //创建进程 CreateProcess( "ChildProc.exe", //新建一个项目childproc。 把exe的生成目录设为和当前程序的生成目录相同 "\"Hello Child\"", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi ); //输出信息 printf( "Process Handle: %p\n", pi.hProcess ); printf( "Process ID: %d\n", pi.dwProcessId ); printf( "Thread Handle: %p\n", pi.hThread ); printf( "Thread ID: %d\n", pi.dwThreadId ); } void Terminate( DWORD dwProcID ) { //打开进程获取句柄 HANDLE hProc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcID ); //结束进程 TerminateProcess( hProc, 0 ); } void Wait( ) { //创建进程 STARTUPINFO si = { 0 }; PROCESS_INFORMATION pi = { 0 }; si.cb = sizeof( si ); CreateProcess( "C:\\Windows\\System32\\Winmine.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); printf( "Winmine is running\n" ); //等候进程结束 WaitForSingleObject( pi.hProcess, INFINITE ); printf( "Winmine is stop\n" ); } int main(int argc, char* argv[]) { //Create( ); //Terminate( 244 ); Wait( ); getch( ); return 0; }
五 Windows作业(Job)
1 Windows作业
实际是一个进程组. 可以对作业设置权限,一旦进程加入到作业之内,进程的权限将被作业限制.
2 作业的使用
需要NT5.0以上支持,所有在Windows.h前定义
#define _WIN32_WINNT 0x0500
2.1 创建一个作业
HANDLE CreateJobObject( LPSECURITY_ATTRIBUTES lpJobAttributes,// 安全属性 LPCTSTR lpName ); //名称
返回创建的Job句柄
2.2 设置作业权限
BOOL SetInformationJobObject( HANDLE hJob,//Job句柄 JOBOBJECTINFOCLASS JobObjectInformationClass,//Job权限的类型 LPVOID lpJobObjectInformation,//类型所对应的数据结构的地址 DWORD cbJobObjectInformationLength //类型所对应的数据结构的长度 );
2.3 将进程加入作业
BOOL AssignProcessToJobObject( HANDLE hJob, //作业句柄 HANDLE hProcess );//进程句柄
2.4 关闭作业
CloseHandle
2.5 结束作业
使用TerminateJobObject结束作业.但是并不是所有情况下,作业内的进程都能被结束.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// WinJob.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "conio.h" #define _WIN32_WINNT 0x0500 #include "windows.h" HANDLE Create( LPSTR pszPath ) { STARTUPINFO si = { 0 }; PROCESS_INFORMATION pi = { 0 }; si.cb = sizeof( si ); CreateProcess( pszPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); return pi.hProcess; } void Job( ) { //创建Job对象 HANDLE hJob = CreateJobObject( NULL, "MyJob" ); //设置权限 JOBOBJECT_BASIC_UI_RESTRICTIONS ui = {0}; ui.UIRestrictionsClass = JOB_OBJECT_UILIMIT_READCLIPBOARD| JOB_OBJECT_UILIMIT_WRITECLIPBOARD; //限制操作剪贴板 SetInformationJobObject( hJob, JobObjectBasicUIRestrictions, //ui的类型与这个参数有关 &ui, sizeof(ui) ); //创建进程 HANDLE hProc = Create( "c:\\windows\\system32\\mspaint.exe" ); //将进程加入作业 AssignProcessToJobObject( hJob, hProc ); getch( ); //结束作业 TerminateJobObject( hJob, 0 ); //关闭Job CloseHandle( hJob ); } int main(int argc, char* argv[]) { Job( ); return 0; }
六 Windows线程
1 Windows线程
Windows进程中可以执行代码的实体,Windows系统可以调度的执行代码.一个进程中至少有一个或多个线程. 每个线程是进程的一个任务分支.
2 线程的特点
2.1 每个线程有一个ID.
2.2 每个线程有自己的安全属性
2.3 每个线程有自己的内存栈.
3 进程和线程多任务
多进程实现的多任务: 由于进程地址空间是属于各自私有, 内存和资源不能共享.
多线程实现的多任务: 由于线程都是位于同一个进程的地址空间,内存和资源可以共享.
4 线程的执行
线程的执行方式采用轮询方式执行.
A -> B -> A -> B.....
七 线程的使用
1 定义线程处理函数
DWORD WINAPI ThreadProc( LPVOID lpParameter ); //线程参数
2 创建线程
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性 DWORD dwStackSize, //初始化栈的大小,缺省为0 LPTHREAD_START_ROUTINE lpStartAddress, //线程的函数指针 LPVOID lpParameter, //线程参数 DWORD dwCreationFlags, //创建方式 LPDWORD lpThreadId //返回线程ID );
返回值是创建好的线程的句柄.
3 结束线程
ExitThread
TerminateThread
4 线程挂起和执行
挂起线程
DWORD SuspendThread( HANDLE hThread );
执行线程
DWORD ResumeThread( HANDLE hThread );
5 等候线程的结束
可以使用 WaitForSingleObject 等候线程的
结束。
6 关闭线程句柄
CloseHandle 句柄关掉不代表结束线程,只是释放线程句柄资源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// ThreadBase.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "conio.h" #include "windows.h" DWORD WINAPI ThreadProc1( LPVOID pParam ) { DWORD nValue = (DWORD)pParam; for( int nIndex=0; nIndex<10; nIndex++ ) { printf( "Thread Proc1-------%d\n", nValue ); Sleep( 1000 ); } return 0; } DWORD WINAPI ThreadProc2( LPVOID pParam ) { while( 1 ) { printf( "-------Thread Proc2\n" ); Sleep( 1000 ); } return 0; } void Create( ) { DWORD nValue = 100; //创建一个挂起的线程 DWORD nThreadID = 0; HANDLE hThread = CreateThread( NULL, 0, ThreadProc1, (LPVOID)nValue, CREATE_SUSPENDED, &nThreadID ); printf( "Thread 1 ID: %d\n", nThreadID ); printf( "Thread 1 Handle: %p\n", hThread ); //执行线程 ResumeThread( hThread ); //等候线程1结束 WaitForSingleObject( hThread, INFINITE ); CloseHandle( hThread ); //创建一个立刻执行的线程 hThread = CreateThread( NULL, 0, ThreadProc2, NULL, 0, &nThreadID ); printf( "Thread 2 ID: %d\n", nThreadID ); printf( "Thread 2 Handle: %p\n", hThread ); //挂起线程 //SuspendThread( hThread ); CloseHandle( hThread ); } int main(int argc, char* argv[]) { Create( ); getch( ); return 0; }
八 线程局部存储 Thread Local Storage
1 由于多个线程使用同一个变量,各个线程都对变量进行操作,那么变量的值会被不同线程操作覆盖。
通常 变量A <-- 线程A
<-- 线程B
TLS 变量A <-- 线程A
变量A <-- 线程B
2 TLS的使用
2.1 使用关键字 __declspec(thread)
__declspec(thread) CHAR * g_pszText2 = NULL;
2.2 TLS相关API
TlsAlloc
TlsSetValue
TlsGetValue
TlsFree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// ThreadTls.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "conio.h" #include "stdlib.h" #include "windows.h" CHAR * g_pszText1 = NULL; //所有子线程共享一个全局变量 //使用关键字定义TLS变量 __declspec(thread) CHAR * g_pszText2 = NULL; void Print( ) { printf( "Text1: %s\n", g_pszText1 ); printf( "Text2: %s\n", g_pszText2 ); } DWORD WINAPI PrintProc( LPVOID pParam ) { CHAR * pszText = (CHAR *)pParam; g_pszText1 = (CHAR *)malloc( 100 ); memset( g_pszText1, 0, 100 ); strcpy( g_pszText1, pszText ); g_pszText2 = (CHAR *)malloc( 100 ); memset( g_pszText2, 0, 100 ); strcpy( g_pszText2, pszText ); while( 1 ) { Print( ); Sleep( 1000 ); } return 0; } void Create( ) { DWORD dwThread = 0; CHAR szText1[] = "Thread 1----------"; HANDLE hThread = CreateThread( NULL, 0, PrintProc, szText1, 0, &dwThread ); CHAR szText2[] = "-----Thread 2-----"; hThread = CreateThread( NULL, 0, PrintProc, szText2, 0, &dwThread ); CHAR szText3[] = "----------Thread 3"; hThread = CreateThread( NULL, 0, PrintProc, szText3, 0, &dwThread ); getch( ); } int main(int argc, char* argv[]) { Create( ); return 0; }