windows核心编程 第四章(2) 进程ios
六、系统版本编程
BOOL GetVersionEx(POSVERSIONINFOEX pVersionInformation);windows
在win7 + VS2010环境下,若是传POSVERSIONINFOEX 类型指针会报错,不能把这个类型转换为POSVERSIONINFOW类型,解决办法是传入POSVERSIONINFOEX后强转为POSVERSIONINFOW类型。安全
1 #include "stdafx.h" 2 3 #include "windows.h" 4 5 #include <iostream> 6 7 using namespace std; 8 9 10 int _tmain(int argc, _TCHAR* argv[]) 11 { 12 13 OSVERSIONINFO osVersion; 14 15 OSVERSIONINFOEX osVersion2; 16 17 18 19 osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); 20 21 osVersion2.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 22 23 24 BOOL bRet = GetVersionEx(&osVersion); 25 26 BOOL bRet2 = GetVersionEx((LPOSVERSIONINFO)&osVersion2); //强转 27 28 29 return 0; 30 31 } 32 33 34 35 36 37 typedef struct{ 38 39 DWORD dwOSVersionInfoSize; //该结构体大小,传入sizeof(该类型) 40 41 DWORD dwMajorVersion;//主版本号 42 43 DWORD dwMinorVersion; //次版本号 44 45 DWORD dwBuildNumber; //构建版本号 46 47 DWORD dwPaltformId; //当前系统支持的套件(suite),值能够是VRE_PLATFORM_WIN32s(Win32s), VER_PLATFORM_WIN32_WINDOWS(win95/98), VER_PLATFORM_WIN32_NT(NT/2000/XP/2003/VISTA) 48 49 TCHAR szCSDVersion[128];//额外的文本,提供了与已安装的操做系统有关的更多信息 50 51 WORD wServicePackMajor;//SP主版本号 52 53 WORD wServicePackMinor; //SP次版本号 54 55 WORDD wSuiteMask; //标识当前系统上可用的suite(s) 56 57 BYTE wProductType; //指出安装的是如下操做系统产品中的哪一个:VER_NT_WORKSTATION, VER_NT_SERVER, VER_NT_DOMAIN_CONTROLLER 58 59 BYTE wReserved; //保留,为0便可 60 61 }OSVERSIONINFOEX, *POSVERSIONINFOEX;
比较主机操做系统是否符合应用程序要求的版本:异步
1 BOOL VerifyVersionInfo( 2 3 POSVERSIONINFOEX pVersionInformation,//见上方 4 5 DWORD dwTypeMask, //比较那些成员(VER_MINORVERSION, VER_MAJORVERSION, VER_BUILDNUMBER, VER_PLATFORMID, VER_SERVICEPACKMINOR, VER_SERVICEPACKMAJOR, VER_SUITENAME, VER_PRODUCT_TYPE); 6 7 DWORDLONG dwlConditionMaks //怎么比较(VER_EQUAL, VER_GREATER, VER_GREATER_EQUAL, VER_LESS或VER_LESS_EQUAL。 对于VER_SUITNAME信息就不能执行这些测试,相反,必须用VER_AND(全部套件都必须安装)或VER_OR(至少安装了其中的一个套件产品)) 8 9 );
dwConditionMask使用一套复杂的位组合对比较方式进行了描述,为了建立恰当的位组合,可使用VER_SET_CONDITION宏:函数
1 VER_SET_CONDITION( 2 3 DWORDLONG dwlConditionMask, //初始为0 4 5 ULONG dwTypeBitMask, //同VerifyVersionInfo中的dwTypeMask,哪一个(不是哪些)要比较的成员,调用屡次这个宏,为每一个须要比较的成员赋值条件掩码(第三个参数) 6 7 ULONG dwConditionMask//条件掩码,注意区分和dwlConditionMask 8 9 )
1 //eg. 2 3 OSVERSIONINFOEX osVersion3; 4 5 osVersion3.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 6 7 8 osVersion3.dwMajorVersion = 6; 9 10 osVersion3.dwMinorVersion = 1; //win7的主次版本号 11 12 osVersion3.dwPlatformId = VER_PLATFORM_WIN32_NT; //要比较哪些成员就该哪些成员赋值; 13 14 15 16 DWORDLONG dwConditionMask = 0; 17 18 VER_SET_CONDITION(dwConditionMask, VER_MAJORVERSION, VER_EQUAL); 19 20 VER_SET_CONDITION(dwConditionMask, VER_MINORVERSION, VER_EQUAL); 21 22 VER_SET_CONDITION(dwConditionMask, VER_PLATFORMID, VER_EQUAL); 23 24 25 26 if (VerifyVersionInfo(&osVersion3, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwConditionMask) ) 27 28 { 29 30 cout<<"It is windows 7"<<endl; 31 32 } 33 34 else 35 36 { 37 38 cout<<"It is not windows 7"<<endl; 39 40 }
七、CreateProcess函数测试
1 BOOL CreateProcess( 2 3 PCTSTR pszApplicationName, //exe名字 4 5 PTSTR pszCommandLine,//命令行参数 6 7 PSECURITY_ATTRIBUTES psaProcess,//进程安全属性 8 9 PSECURITY_ATTRIBUTES psaThread, //进程的主线程安全属性 10 11 BOOL hInheritHandles, //是否容许该进程内核对象句柄被继承(最后一个参数中有该进程和主线程的内核对象句柄,若是不用直接CloseHandle) 12 13 DWORD fdwCreate,//标记 14 15 PVOID pvEnvironment, 16 17 PCTSTR pszCurDir,//进程当前目录 18 PSTARTUPINFO psiStartInfo, //进程启动信息 19 20 PPROCESS_INFORMATION ppiProcInfo//进程有关信息结构体,包含进程ID,进程句柄,线程ID,线程句柄 21 22 );
建立进程时,系统为新进程建立一个虚拟地址空间,并将可执行文件(和全部必要的DLL)的代码和数据加载到进程的地址空间中。ui
须要注意的是,当函数成功时返回TRUE,可是当建立的子进程还没有初始化好以前就返回TRUE,若是此子进程须要的DLL没有加载成功就会致使子进程建立不成功,而此时函数已经返回了TRUE,因此只是个须要注意的问题!spa
7.1 参数:操作系统
pszApplicationName:可执行文件名,一般为NULL,可执行文件名和参数都由pszCommandLine来传入。
pszCommandLine:要传给新进程的命令行字符串,类型为PTSTR,非CONST类型,意味着函数CreateProcess内部会修改这个参数,在CreateProcess函数返回以前它会将这个字符串还原为原来的形式。因此咱们在给这个参数传参的时候不要直接传一个常量字符串,而要把常量字符串放在一个临时缓冲区中,当CreataProcess解析pszCommandLine字符串时它会检查字符串中的第一个标记(token),并假定它是咱们运行的可执行文件的名字,若是可执行文件没有扩展名则默认是.exe扩展名。
最好使用VS编译器的/GF开关和一个临时缓冲区。
C/C++运行时库启动时会检查进程的命令行,将可执行文件名以后的第一个实参的地址传给(w)WinMain的pszCmdLine参数。
若是pszApplicationName为NULL的时候会执行上述操做,当pszApplicationName不为空的时候,pszApplicationName必须自定扩展名.exe,不能够省略,若是省略了会出错。
但同时pszApplicationName和pszCommandLine都不为空则不能同时都给这两个参数传可执行文件,若是pszApplicationName指定了可执行文件,pszCommandLine也指定了这可执行文件,则报以下的错误,无论pszCommandLine中有没有指定.exe扩展名。
1 void CCreateProcessDlg::OnBnClickedButton1() 2 { 3 4 // TODO: 在此添加控件通知处理程序代码; 5 6 TCHAR szApplication[] = _T("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\devenv.exe"); 7 8 TCHAR szCmdLine[] = _T("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\devenv \ 9 10 D:\\Files\\VS2010-MyProjects\\windowsCore2\\windowsCore2.sln"); 11 12 13 14 STARTUPINFO startInfo = {sizeof(startInfo)};//必定要初始化为0; 15 16 STARTUPINFOEX startInfoEx = {sizeof(startInfoEx)}; 17 18 19 PROCESS_INFORMATION proInfo; //进程信息结构体; 20 21 22 SECURITY_ATTRIBUTES saProcess; 23 24 saProcess.bInheritHandle = FALSE; //该进程内核对象不能被继承; 25 26 saProcess.lpSecurityDescriptor = NULL; 27 28 saProcess.nLength = sizeof(saProcess); 29 30 31 SECURITY_ATTRIBUTES saThread; 32 33 saThread.bInheritHandle = FALSE; 34 35 saThread.lpSecurityDescriptor = NULL; 36 37 saThread.nLength = sizeof(saThread); 38 39 40 BOOL bRet = CreateProcess( 41 42 NULL, /* szApplication ,*/ 43 44 szCmdLine, 45 46 &saProcess, 47 48 &saThread, 49 50 FALSE, 51 52 CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS | EXTENDED_STARTUPINFO_PRESENT, //初始时进程的主线程挂起,等待主调进程调用ResumeThread; 53 54 NULL, 55 56 NULL, 57 58 /*&startInfo,*/ &startInfoEx.StartupInfo, //fdwCreate指定了EXTENDED_STARTUPINFO_PRESENT 59 60 &proInfo 61 62 ); 63 64 65 if (TRUE == bRet) 66 67 { 68 69 ResumeThread(proInfo.hThread); 70 71 CloseHandle(proInfo.hThread); 72 73 CloseHandle(proInfo.hProcess); 74 75 } 76 77 else 78 79 { 80 81 DWORD dwErr = GetLastError(); 82 83 CString strInfo; 84 85 strInfo.Format(_T("lasterror=%u"), dwErr); 86 87 AfxMessageBox(strInfo); 88 89 } 90 91 }
fdwCreate参数:
这个参数标识了影响新进程建立方式的标志。
DEBUG_PROCESS
DEBUG_ONLY_THIS_PROCESS
CREATE_SUSPENDED //建立子进程后子进程的主线程挂起,等待主调进程调用ResumeThread
DETACHED_PROCESS //当子进程是一个CUI程序时该表示表示子进程新建立一个控制台窗口,不使用父进程的控制台窗口
CREATE_NEW_CONSOLE //系统为新进程建立一个新的控制台窗口,不能和DETACHED_PROCESS同时用
CREATE_NO_WINDOW //不建立任何控制台窗口
CREATE_NEW_PROCESS_GROUP
CREATE_DEFAULT_ERROR_MODE //子进程不会继承父进程的错误模式(SetErrorMode)
CREATE_SEPARATE_WOW_VDM
CREATE_SHARED_WOW_VDM
CREATE_UNICODE_ENVIRONMENT //表示子进程的环境块包含UNICODE字符,进程的环境块默认包含的是ANSI字符串
CREATE_FORCEDOS //DOS年代的东西
CREATE_BREAKAWAY_FROM_JOB //标志容许一个做业中的进程生成一个和做业无关的进程EXTENDED_STARTUPINFO_PRISENT //表示CreateProcesss中给psiStartInfo参数传的是一个STARTUPINFOEX结构体(若是是STARTUPINFOEX结构体指针,则须要取其中的STARTUPINFO成员,不然不能建立子进程,GetLastError为87(参数错误)。
另外还有一些关于进程优先级的标志:
IDLE----IDLE_PRIORITY_CLASS
Below normal----BELOW_NORMAL_PRIORITY_CLASS
Normal----NORMAL_PRIORITY_CLASS
Above Normal----ABOVE_NORMAL_PRIORITY_CLASS
High----HIGH_PRIORITY_CLASS
RealTime----REALTIME_PRIORITY_CLASS
pvEnvironment参数
该参数指向一块内存,其中包含新进程要使用的环境字符串,大多数时候传入NULL便可,这将致使子进程继承父进程的一组环境字符串。
得到主调进程的环境字符串地址:
PVOID GetEnvironmentStrings(); //内部分配内存
BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock); //释放内存
1 //eg. 2 3 PTSTR psEnv = (PTSTR)GetEnvironmentStrings(); 4 5 AfxMessageBox(CString(psEnv)); 6 7 FreeEnvironmentStrings(psEnv);
pszCurDir参数
进程当前驱动器和目录,若是为NULL则和父进程有一样的当前驱动器和目录,若是不为NULL,则必须至少指定一个驱动器。
psiStartInfo参数
该参数指向一个STARTUPINFO或一个STARTUPINFOEX结构,取决于fdwCreate中是否指定EXTENDED_STARTUPINFO_PRESENT标志,若是指定了该标记则为后者。
该参数结构体必须初始化其值,由于这些值可能会致使子进程建立失败,至少要作的是为第一个成员DWROD cb(结构体字节大小)赋值为sizeof(STARTUPINFO或STARTUPINFOEX),
其余成员都为0, 可以下:
1 STARTUPINFO startInfo = {sizeof(STARTUPINFO)}; 2 3 4 5 typedef struct _STARTUPINFO { 6 7 DWORD cb; //CUI, GUI大小 8 9 LPTSTR lpReserved; //CUI, GUI保留, NULL 10 11 LPTSTR lpDesktop; //CUI, GUI指定哪一个桌面 12 13 LPTSTR lpTitle; //CUI,窗口标题 14 15 DWORD dwX; 16 17 DWORD dwY; //CUI,GUI,略 18 19 DWORD dwXSize; 20 21 DWORD dwYSize; //CUI, GUI 22 23 DWORD dwXCountChars; //CUI,控制台窗口的宽度和高度(字符数来表示) 24 25 DWORD dwYCountChars; //CUI,控制台窗口的宽度和高度(字符数来表示) 26 27 DWORD dwFillAttribute;//CUI, 控制台窗口所用的文本和背景色 28 29 DWORD dwFlags; //CUI, GUI,详见后面 30 31 WORD wShowWindow; //GUI,在某exe上右键属性:运行方式(常规,最大化,最小化) 32 33 WORD cbReserved2; //CUI, GUI, 保留,0 34 35 LPBYTE lpReserved2; //保留, NULL 36 37 HANDLE hStdInput; //CUI 38 39 HANDLE hStdOutput; //CUI 40 41 HANDLE hStdError; //CUI 42 43 } STARTUPINFO, *LPSTARTUPINFO;
关于STARTUPINFO的dwFlags成员:
该成员表示了使用该STARTUPINFO结构体中的哪些成员,即忽略哪些成员。
下面表示: 表示和对应的使用的那些成员
STARTF_USESIZE:dwSXize, dwYSize
STARTF_USESHOWWINDOW: wShowWindow
STARTF_USEPOSITION:dwX, dwY
STARTF_USECOUNTCHARS: dwXCountChars, dwYCountChars
STARTF_USEFILLLATTRIBUTES: dwFillAttribute
STARTF_USESTDHANDLES:hStdInput, hStdOutput, hStdError
STARTF_FORCEONFEEDBACK //鼠标变为忙碌,鼠标本身会变回来
STARTF_FORCEOFFFEEDBACK //不让或关闭鼠标变为忙碌
对某GUI程序右键时,属性能够看到运行方式,值即为WShowWindow的值。
1 typedef struct _STARTUPINFOEX { 2 3 STARTUPINFO StartupInfo; 4 5 PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; 6 7 } STARTUPINFOEX, *LPSTARTUPINFOEX;
此结构略,详见《windows核心编程》第五版,第96页。
ppiProcInfo参数
1 typedef struct_PROCESS_INFORMATION{ 2 HANDLE hProcess; 3 4 HANDLE hThread; 5 6 DWORD dwProcessId; 7 8 DWORD dwThreadId; 9 10 }PROCESS_INFORMATION;
其中成员含义以下。
① hProcess:返回新进程的句柄。
② hThread:返回主线程的句柄。
③ dwProcessId:返回一个全局进程标识符。该标识符用于标识一个进程。从进程被
建立到终止,该值始终有效。
④ dwThreadId:返回一个全局线程标识符。该标识符用于标识一个线程。从线程被创
建到终止,该值始终有效。
Windows任务管理器里PID为0的进程为【System Idel Process】, 该进程中的线程数量为逻辑CPU的数量。
得到当前进程ID
GetCurrentProcessId()
得到当前线程ID
GetCurrentThreadId()
根据进程内核对象句柄得到进程ID
GerProcessId(HANDLE hProcess);
根据线程内核对象句柄得到线程ID
GetThreadId(HANDLE hThread)
根据线程内核对象句柄得到该线程所在进程的进程ID
GetProcessIdOfThread(HANDLE hThread)
注意:父子进程的关系只有在建立瞬间才有,建立以后就不存在父子关系了,ToolHelp函数容许进程经过PROCESSENTRY32结构体来查询其父进程,其th32ParentProcessID成员记录的就是子进程的父进程ID,可是因为进程ID会被复用,所以结果可能不许确。
若是不用CloseHandle函数来关闭进程内核对象的句柄,则父进程的ID就不会被复用。
1 typedef struct tagPROCESSENTRY32 2 { 3 4 DWORD dwSize; 5 6 DWORD cntUsage; 7 8 DWORD th32ProcessID; 9 10 ULONG_PTR th32DefaultHeapID; 11 12 DWORD th32ModuleID; 13 14 DWORD cntThreads; 15 16 DWORD th32ParentProcessID; //其父进程进程ID(进程ID会被重用,所以可能不许确) 17 18 LONG pcPriClassBase; 19 20 DWORD dwFlags; 21 22 TCHAR szExeFile[MAX_PATH]; 23 24 } PROCESSENTRY32, *PPROCESSENTRY32;
八、终止进程
四种方式:
① 主线程的入口点函数返回(强烈推荐, strongly recommanded)
② 进程中的一个线程调用ExitProcess函数(要避免)
③ 另外一个进程中的线程调用TerminateProcess函数(避免)
④ 进程中全部的线程都【天然死亡】(几乎从不会发生)
8.1 ExitProcess
VOID ExitProcess(UINT fuExitCode); //退出当前进程,参数为退出代码
当主线程的的入口点函数(WinMain, wWinMain, main, wmain)返回时,会返回到C/C++运行库启动代码,后者将正确清理进程使用的所有C/C++运行时资源。 释放了C运行时资源以后,C运行时启动代码将显示调用ExitProcess,并将入口点函数返回的值传给他,即退出代码。
C/C++运行库:无论进程中是否还有其余线程正在运行,只要应用程序的主线程从它的入口点函数返回,C/C++运行库就会调用ExitProcess来终止进程,可是,若是在入口点函数中调用的是ExitThread而不是调用ExitProcess或者入口点函数直接返回,应用程序住线程将中止执行,但只要进程中还有其余线程正在运行,进程就不会终止。
只要从主线程的入口点函数返回,C/C++运行时就能执行其清理工做,并正确析构全部的C++对象。
若是显示调用ExitProcess可能致使C++对象或者其余资源不能获得清理。
8.2 TerminateProcess
1 BOOL TerminateProcess( 2 3 HANDLE hProcess, //要关闭的进程的句柄 4 5 UINT fuExitCode //退出代码 6 7 );
该函数是异步的,当该函数返回的时候并不能保证要关闭的进程已经终止。
8.3
若是进程中的每一个线程都调用了ExitThread函数来终止当前线程,那么进程的退出代码将会是最后一个线程的退出代码。
进程终止时要作的工做:
① 终止进程中遗留的任何线程
② 释放全部用户对象和GDI对象,关闭全部内核对象句柄(根据引用计数决定是否销毁)
③ 进程的退出代码从STILL_ACTIVE变为传给ExitProcess或TerminateProcess函数的代码
④ 进程内核对象的状态变为【已触发】
⑤ 进程内核对象的引用计数减1
关于子进程: 进程间通讯的方式有剪贴板、内存文件映射、命名管道、匿名管道(仅父子进程间)、邮槽等。
//得到某个进程的退出代码
BOOL GetExitCodeProcess(HANDLE hProcess, PDWORD pExitCode);
1 TCHAR szParam = _T(“XXX.exe 参数1 参数2 … 参数N”); 2 3 PROCESS_INFORMATION proInfo; 4 5 BOOL bRet = CreateProcess(NULL, szParam,…..,&proInfo); 6 7 If(TRUE == bRet) 8 { 9 10 CloseHandle(proInfo.hThread); //关闭子进程的主线程内核对象句柄 11 12 13 14 WaitForSingleObject(proInfo.hProcess, INFINITE);//当proInfo.hProcess标识的进程终止时会设置内核对象状态为【已触发】 15 16 17 18 DWORD dwExitCode = 0; 19 20 GetExitCodeProcess(proInfo.hProcess, &dwExitCode); //得到子进程的退出代码, 若是调用GetExitCodeProcess时标识的进程尚未终止,则函数调用STILL_ACTIVE(值为0x103)标识符来填充dwExitCode的值。 21 22 23 24 CloseHandle(proInfo.hProcess); 25 26 }
若是要切断父子进程之间的全部联系,windows资源管理器必须调用CloseHandle来关闭新进程及其主线程的句柄。
CloseHandle(proInfo.hProcess);
CloseHandle(proInfo.hThread);