zoukankan      html  css  js  c++  java
  • 【windows核心编程】 第四章(2) 进程

    windows核心编程 第四章(2) 进程

    6、系统版本

     

    BOOL  GetVersionEx(POSVERSIONINFOEX  pVersionInformation);

    在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     }

     

     

     

     

    7、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)的代码和数据加载到进程的地址空间中。

     

    需要注意的是,当函数成功时返回TRUE,但是当创建的子进程尚未初始化好之前就返回TRUE,如果此子进程需要的DLL没有加载成功就会导致子进程创建不成功,而此时函数已经返回了TRUE,所以只是个需要注意的问题!

     

     

    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;

     

     

    8、终止进程

    四种方式:

    ①    主线程的入口点函数返回(强烈推荐, 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);

  • 相关阅读:
    python 格式化 json输出
    python
    回顾2013
    C扩展Python
    C扩展Python
    Python
    Python interview preparing
    Python用smtplib发送邮件
    Python 安装路径, dist-packages 和 site-packages 区别
    nginx worker_cpu_affinity使用方法
  • 原文地址:https://www.cnblogs.com/cuish/p/3099929.html
Copyright © 2011-2022 走看看