1:一个进程由两部分组成,1是进程内核对象,这也是操作系统保存进程统计信息的地方,2是一个地址空间;线程在进程上下文中运行,每个线程都有自己的一组CUP寄存器和堆栈
2:系统会根据连接器开关寻找_tWinMain()或者_tmain(),并调用相应的C/C++运行库启动函数,后者调用_tWinMain()或者_tmain(),如果没有指定连接器开关,则会去搜索_tWinMain()或者_tmain()
3:创建进程代码
STARTUPINFO si={sizeof(si)};
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(pi));
TCHAR szBuf[]=_T("WEBxysj.exe");
bool ret=CreateProcess(NULL,szBuf,NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
3:启动函数完成的主要任务:
初始化C/C++运行库全局变量
初始化C运行库内存分配函数
调用所有全局和静态C++类对象构造函数
4:启动函数返回后完成以下主要任务
调用_onexit函数调用所注册的任何一个函数
调用所有全局和静态C++类对象的析构函数
在DEBUG中,如果设置了_CRTDBG_LEAK_CHECK_DF标志,通过_CrtDumpMemoryLeaks函数生成内存泄露报告
调用ExitProcess函数,传入主函数返回值
5:进程实例句柄
进程实例句柄和进程内核对象是两个不同的概念,如果是GUI程序,WinMain()第一个参数HINSTANCE就是进程实例句柄,HINSTANCE和HMODULE是同一个东西,他们实际上是一个内存基地址,系统将可执行文件映像加载到进程地址空间的这个位置,VS默认的地址是0x00400000
GetModuleHandle(strName)返回一个可执行文件或者DLL被加载到进程空间什么地方;如果strName为NULL,则返回进程实例句柄,如果GetModulHandle(NULL)是在一个DLL中运行,同样返回加载此DLL进程的进程实例句柄
GetModuleFileName(hModule,pszPath,size);获取一个可执行文件路径
6:WinMain()的参数hPrevInstance总是为NULL,目的是兼容16位windows系统
7:系统在创建一个新进程时,会传递一个命令行给他,在WinMain()中就是pszCmdLine参数,在调用C启动函数时,后者会调用GetCommandLine()获得命令行,忽略其中的可执行文件名称,并将剩余部分传递给应用程序pszCmdLine参数
8:进程环境块
每个进程都有其相关的进程环境块
用户登录windows时,系统会创建外壳(Shell)进程,它的进程环境块在我的电脑右键-环境变量中,这些环境变量被定义在注册表中
子进程会获得父进程进程环境块,父进程可以控制那些环境块可以被继承,子进程继承的是父进程环境块的一个副本,所以子进程的修改不会影响到父进程
获取一个环境变量值代码如下:
PTSTR p=NULL;
DWORD dwSize=GetEnvironmentVariable(strName,p,0);//第三个参数传入0表示获得strName对应环境变量长度
p=new char(dwSize);
GetEnvironmentVariable(strName,p,dwSize);
添加或者修改或者移除一个环境变量值函数是:
SetEnvironmentVariable(strName,strValue);如果strValue==NULL,则移除此环境变量
在注册表中,经常看见%xxx%这样的字符串,xxx就是一个环境变量,系统会自动执行字符串替换
获取%xxx%对应的字符串代码如下(我运行失败了,dwSize长度不够,很奇怪),如果dwSize不够,szBuf会为空
DWORD dwSize=ExpandEnvironmentStrings(TEXT("PATH='%PATH%'"),NULL,0);
TCHAR *szBuf=new TCHAR[dwSize];
dwSize=ExpandEnvironmentStrings(TEXT("PATH='%PATH%'"),szBuf,dwSize);
9:子进程会继承父进程CPU关联性
10:SetErrorMode()可以设置进程错误模式,子进程继承父进程错误模式,如果要关闭这种继承,在CreateProcess中关闭
11:获取和设置进程当前驱动器和目录
TCHAR szBuf[MAX_PATH]={0};
GetCurrentDirectory(MAX_PATH,szBuf);
SetCurrentDirectory("E:\XFQ");
12:进程环境变量可以维护进程其他驱动器目录
获得一个驱动器当前目录代码如下
TCHAR szBuf[MAX_PATH]={0};
GetFullPathName(TEXT("C:"),MAX_PATH,szBuf,NULL);
13:GetVersionEx获得操作系统版本信息
14:创建进程
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
);
CreateProcess()会创建一个进程,并初始化进程内核对象和线程内核对象,使其引用计数都为1,如果成功创建了新进程和主线程,函数返回TRUE,但新进程DLL还在继续加载,可能会失败,如果失败,则新进程就会终止
14.1:如果进程名在pszCommandLine中,pszCommandLine必须是可读写的,并且可以不写.exe扩展名,并且会搜索目录,顺序是:
主进程exe所在目录
主进程当前目录
System32文件夹
Windows目录
PATH环境变量目录
如果进程名在pszApplicationName中,则必须写.exe后缀,并且只会查看进程当前目录
14.2:lpProcessAttributes和lpThreadAttributes可以控制新进程的进程内核对象和新进程的线程内核对象是否是可继承的
14.3:dwCreationFlags标志可以控制如下:
父进程是否希望以调试方式运行新进程
创建新进程的时候是否挂起新进程
指定进程优先级
14.4:lpEnvironment如果为NULL,子进程继承父进程进程环境块
14.5:lpCurrentDirectory允许父进程设置子进程当前驱动器和目录
14.6:lpStartupInfo传入STARTUPINFO或者STARTUPINFOEX结构,STARTUPINFOEX能够多控制如下应用
1:筛选可继承句柄列表,只继承自己想要的句柄
2:指定一个进程(不是调用CreateProcess的进程),新进程以他为父进程,并继承他的环境变量,可继承句柄等,但这种指定父进程不会改变新进程和创建进程的调试关系(如果存在)
14.7:CreateProcess返回之前,会用完全访问权限打开进程内核对象和线程内核对象,并把他们放进lpProcessInformation中,这时,这两个内核对象使用计数都为2
系统会为进程和线程分配一个ID,这两个ID使用同一个号码池,获得进程和线程ID如下代码
GetCurrentProcessId();
GetProcessId(HANDLE hd);
GetCurrentThreadId();
GetThreadId(Handle hd);
GetProcessIdOfThread(HANDLE hd);//根据线程句柄获得进程ID
15:进程的父子关系只在创建的一瞬间维持,但子进程确实保存了一个父进程ID,但因为ID是会被重用的,所有查询结果也是不准确的,ToolHelp包含一个查询父进程ID的函数
16:终止进程方式
16.1:主线程返回.系统会执行以下操作
C++对象会被正确析构
释放线程栈
主线程函数返回的值,将被设置为进程退出代码,这个退出代码在进程内核对象中维护
递减进程使用计数
16.2:调用void ExitProcess(UINT fuExitCode)终止进程
对操作系统而言,一个进程在所有线程终止后才会终止,但C/C++运行库采用了不同的策略,当主线程退出后,C/C++运行库执行清理工作,然后调用ExitProcess()终止进程,并将主线程返回值作为ExitProcess()的参数传入,所以主线程是针对C/C++运行库而言的
fuExitCode被设置问进程退出代码
16.3:调用BOOL TerminateProcess(HANDLE hd,UINT fuExitCode)终止进程
此函数是异步的,调用了此函数后,要继续调用WaitForSingleObject(),将进程句柄传递给它,才能确定进程是否真正被终止
16.4:当进程中所有线程被终止时,进程退出,进程退出代码会被设置为最后一个线程退出代码
17:当进程终止运行时
会执行如下操作
1:终止所有遗留线程
2:释放遗留GDI,句柄等资源
3:将退出代码从STILL_ACTIVE(0x103)变为进程退出代码,并将内核对象设置为已触发状态
4:进程内核对象使用计数-1
任何时候,可以通过GetExitCodeProcess(HANDLE,PDWORD)获得一个进程的退出代码,如果进程还未退出,则退出代码为STILL_ACTIVE
18:创建一个进程,等待它退出,并检查返回代码
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess,INFINITE);
int nExitCode=0;
GetExitCodeProcess(pi.hProcess,&nExitCode);
CloseHandle(pi.hProcess);