【windows核心編程】 第四章(2) 進程

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);

相關文章
相關標籤/搜索