進(jìn)程由兩部分組成:
- 操作系統(tǒng)管理進(jìn)程的內(nèi)核對(duì)象。存放該進(jìn)程 的統(tǒng)計(jì)信息的地方。
- 地址空間,包含可執(zhí)行模塊和DLL模塊的代碼和數(shù)據(jù)。動(dòng)態(tài)分配的內(nèi)存(線程堆棧和堆)。
進(jìn)程是不活潑的,進(jìn)程當(dāng)中至少要有一個(gè)線程,每個(gè)線程要有自己的堆棧和自己的CPU寄存器。CPU通過算法給每個(gè)線程分配時(shí)間片的辦法來造成假象是在同時(shí)工作(多核通過自己的算法實(shí)現(xiàn)同時(shí)運(yùn)行)。
4.1 編寫第一個(gè)Windiws應(yīng)用程序
Windows兩種類型的程序:
- CUI程序,比如CMD.EXE等等。Microsoft Visual C++連接開關(guān)為/SUBSYSTEM:CONDOLE(程序啟動(dòng)時(shí)不能創(chuàng)建GUI程序)。
- GUI程序,圖形用戶程序,比如Notepad,Word等等。Microsoft Visual C++連接開關(guān)為/SUBSYSTEM:WINDOWS(程序啟動(dòng)時(shí)不能創(chuàng)建CUI程序)。
注意:倆者的概念其實(shí)是很模糊的,CUI可以創(chuàng)建GUI圖形界面,反之GUI程序可能用CUI程序。
Windows進(jìn)入點(diǎn)函數(shù)(區(qū)分在于CUI和GUI程序,ANSI碼和UNICODE碼)
int WINAPI WinMain(
HINSTANCE hinstExe,
HINSTANCE,
PSTR pszCmdLine,
int nCmdShow);
int WINAPI wWinMain(
HINSTANCE hinstExe,
HINSTANCE,
PWSTR pszCmdLine,
int nCmdShow);
int __cdecl main(
int argc,
char *argv[],
char *envp[]);
int __cdecl wmain(
int argc,
wchar_t *argv[],
wchar_t *envp[]);
其實(shí)Windows程序啟動(dòng)時(shí)最開始并不調(diào)用自己寫的入口函數(shù),而是調(diào)用系統(tǒng)的幾個(gè)入口函數(shù),以便可以調(diào)用malloc和free之類的函數(shù),初始化全局和靜態(tài)C++對(duì)象等。
- 檢索指向新進(jìn)程的完整命令行的指針。
- 檢索指向新進(jìn)程的環(huán)境變量的指針。
- 對(duì)C/C++運(yùn)行期的全局變量進(jìn)行初始化。如果包含StdLib.h文件,代碼就可以訪問這些變量。
- 對(duì)C運(yùn)行期的malloc和callo和其他底層輸入/輸出例程使用的內(nèi)存棧進(jìn)行初始化。
- 為所有全局和靜態(tài)C++類對(duì)象調(diào)用構(gòu)造函數(shù)。
應(yīng)用程序類型 進(jìn)入點(diǎn) 嵌入可執(zhí)行文件的啟動(dòng)函數(shù)
ANSI碼GUI應(yīng)用程序 WinMain WinMainCRTStattup
UNICODE碼GUI應(yīng)用程序 wWinMain wWinMainCRTStattup
ANSI碼CUI應(yīng)用程序 main mainCRTStattup
UNICODE碼CUI應(yīng)用程序 wmain wmainCRTStattup
注意:應(yīng)用程序會(huì)根據(jù)SUBSYSTEM開關(guān)來查找嵌入可執(zhí)行啟動(dòng)函數(shù),如果進(jìn)入點(diǎn)函數(shù)和啟動(dòng)函數(shù)不匹配則顯示鏈接錯(cuò)誤??梢詣h除SUBSYSTEM(VS Project Settings)開關(guān),這樣應(yīng)用程序會(huì)自動(dòng)需找匹配的函數(shù)。
進(jìn)入點(diǎn)函數(shù)返回時(shí)調(diào)用系統(tǒng)的exit函數(shù),將返回值傳遞給它。exit函數(shù)負(fù)責(zé)下面操作:
- 調(diào)用由_onexit函數(shù)的調(diào)用而注冊(cè)的任何函數(shù)。
- 為所有全局的和靜態(tài)的C++類對(duì)象調(diào)用析構(gòu)函數(shù)
- 調(diào)用操作系統(tǒng)的ExitProcess,并將返回值傳遞給他,關(guān)閉進(jìn)程。
4.1.1 進(jìn)程的實(shí)例句柄
WinMain/wWinMain函數(shù)的第一個(gè)參數(shù)表示進(jìn)程加載的可執(zhí)行文件的基地址/句柄。對(duì)于加載資源的調(diào)用都要使用此句柄,比如HICON LoadIcon(HINSTANCE, PCTSTR)。有的函數(shù)需要使用HMODULE,和HINSTANCE是一個(gè)意思(區(qū)分主要在于16位的操作系統(tǒng)中)。
HMODULE GetModuleHandle(PCTSTR pszModele);
函數(shù)作用,返回加載調(diào)用進(jìn)程中的可執(zhí)行文件或者DLL的基地址/句柄,參數(shù)是可執(zhí)行文件或者DLL的名稱。給pszModule賦值NULL,則返回的是進(jìn)程中可執(zhí)行文件的句柄。
注意:如果找不到則返回NULL。如果在DLL中傳遞NULL,返回的仍然是進(jìn)程加載的可執(zhí)行文件的句柄。
4.1.2 進(jìn)程的前一個(gè)實(shí)例句柄
第二個(gè)參數(shù)都傳遞NULL,是為16位系統(tǒng)所保留的。
4.1.3 進(jìn)程的命令行
注意:不要試圖修改命令行內(nèi)部?jī)?nèi)存的值,要使用修改先拷貝出來。
PTSTR GetCommandLine(); // 返回命令行字符串
PTSTR CommandLineToArgv(PTSTR pszCmdLine, int *pNumArgs); // 拆分命令行字符串函數(shù)
Demo:
int nNumargs;
PTSTR *ppArgv = CommandLineToArgv(GetCommandLine(), &nNumargs);
if ('x' == *ppArgv[1]) {
// TODO:
}
// 手動(dòng)釋放內(nèi)存,一般不需要釋放,系統(tǒng)會(huì)進(jìn)程關(guān)閉時(shí)候自動(dòng)釋放
HeapFree(GetProcessHeap, 0, ppArgv);
4.1.4 進(jìn)程的環(huán)境變量
環(huán)境塊是進(jìn)程地址空間中分配的內(nèi)存塊每個(gè)環(huán)境塊都包含一組字符串,格式如下:
VarName1=VarVarlue1\0
VarName2=VarVarlue2\0
VarName3=VarVarlue3\0
…..
VarNameX=VarVarlueX\0
\0
注意:
- 排序必須按照字母順序。
- ‘=’號(hào)不能是變量名的一部分。
- 等號(hào)左右兩邊的空格將被算做名稱或者值。
- 最后必須加個(gè)’\0’表示結(jié)束。
- 子進(jìn)程和父進(jìn)程不共用環(huán)境塊,修改不會(huì)影響父/子進(jìn)程。
DWORD GetEnvironmentVariable(PCTSTR pszName, PTSTR pszValue, DWORD cchValue);
pszName指變量名,pszValue指向變量值的緩存區(qū),cchValue緩存區(qū)的大小。找不到變量名或者設(shè)置的長(zhǎng)度不夠存放就返回0。
ExpandEnvironmentStrings(PCSTR pszSrc, PSTR pszDst, DWORD nSize);
用來用現(xiàn)實(shí)出可替換的環(huán)境變量的字符串。
BOOL SetEnvironmentVariable(PCTSTR pszName, PCTSTR pszValue);
設(shè)置環(huán)境變量的值,如果不存在則創(chuàng)建,如果存在則替換他的值。
4.1.5 進(jìn)程的親緣性
子進(jìn)程繼承父進(jìn)程的親緣性。(具體什么意思沒明白)
4.1.6 進(jìn)程的錯(cuò)誤模式
進(jìn)程可以設(shè)置如何處理一些錯(cuò)誤。
UINT SetErrorMode(UINT fuErrorMode);
各個(gè)模式用OR連接
標(biāo)志 說明
SEM_FAILCRITICALERRORS 系統(tǒng)不顯示關(guān)鍵錯(cuò)誤句柄消息框,并將錯(cuò)誤返回給調(diào)用進(jìn)程
SEM_NOGOFAULTERRORBOX 系統(tǒng)不顯示一般保護(hù)故障消息框。本標(biāo)志只應(yīng)該由采用異常情況處理程序來處理一般保護(hù)(GP)故障的調(diào)式應(yīng)用程式來設(shè)定
SEM_NOOPENFILEERRORBOX 當(dāng)系統(tǒng)找不到文件時(shí),它不顯示消息框。
SEM_NOALIGNMENTFAULTEXCEPT 系統(tǒng)自動(dòng)排除內(nèi)存沒有對(duì)其的故障,并使應(yīng)用程序看不到這些故障。本標(biāo)志對(duì)X86處理器不起作用。
子進(jìn)程繼承父進(jìn)程的錯(cuò)誤模式,如果不想讓子進(jìn)程繼承父進(jìn)程的錯(cuò)誤模式的話,可以再調(diào)用CreateProcess時(shí)設(shè)定CREATE_DEFAULT_ERROR_MODE標(biāo)志。
4.1.7 進(jìn)程的當(dāng)前驅(qū)動(dòng)器和目錄
默認(rèn)情況下不提供全路徑的話,系統(tǒng)就會(huì)在當(dāng)前驅(qū)動(dòng)器和目錄中查找文件,比如CreateFile,因?yàn)轵?qū)動(dòng)器和目錄是每個(gè)進(jìn)程來維護(hù)的,所以某個(gè)線程改變了目錄和驅(qū)動(dòng)器會(huì)改變整個(gè)進(jìn)程的目錄和驅(qū)動(dòng)器。
下面兩個(gè)函數(shù)讀取和設(shè)置:
DWORD GetCurrentDirectory(DWORD cchCurDir, PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
4.1.8 進(jìn)程的當(dāng)前目錄
驅(qū)動(dòng)器環(huán)境塊的格式:
=C:=C:\Utility\Bin
程序查找驅(qū)動(dòng)器環(huán)境塊,如果沒有則按驅(qū)動(dòng)器名查找。
子進(jìn)程不能繼承父進(jìn)程的驅(qū)動(dòng)器塊,如果想繼承必須寫到環(huán)境變量中去。(好像是這樣,如果有不對(duì)請(qǐng)高人指點(diǎn))。
DWORD GetFullPathName(PCTSTR pszFile, DWORD cchPath, PTSTR pszPath, PTSTR *ppszFilePart);
獲取驅(qū)動(dòng)器的當(dāng)前目錄,比如:
TCHAR szCurDir[MAX_PATH];
DWORD GetFullPathName(TEXT("C:"), MAX_PATH, szCurDir, NULL);
4.1.9 系統(tǒng)版本
DWORD GetVersion();此函數(shù)存在高地位的混論BUG,所以盡量不要使用。
BOOL GetVersion(POSVERSIONINFOEX pVersionInfomation);
typedef struct _OSVERSIONINFOEXA {
DWORD dwOSVersionInfoSize; // 在調(diào)用GetVersionEx函數(shù)之前,必必須置為sizeof(OSVERSIONINFOEX)
DWORD dwMajorVersion; // 主系統(tǒng)的主要版本號(hào)
DWORD dwMinorVersion; // 主系統(tǒng)的次要版本號(hào)
DWORD dwBuildNumber; // 當(dāng)前系統(tǒng)的構(gòu)建號(hào)
DWORD dwPlatformId; // 識(shí)別當(dāng)前系統(tǒng)的平臺(tái)。可以使VER_PLATFORM_WIN32(WIN32),VER_PLATFORM_WIN32_WINDOWS(WINDOWS 95/WINDOWS 98),VER_PLATFORM_WIN32_NT(WINDOWS NT/WINDOWS 2000)或VER_PLATFORM_WIN32_CEHH(WINDOWS CE)
CHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usage 本域包含了附加文本,用于提供關(guān)于已經(jīng)安裝的操作系統(tǒng)的詳細(xì)信息
WORD wServicePackMajor; // 最新安裝的服務(wù)程序包的主要版本號(hào)
WORD wServicePackMinor; // 最新安裝的服務(wù)程序包的次要版本號(hào)
WORD wSuiteMask; // 用于標(biāo)識(shí)系統(tǒng)上存在那個(gè)程序組(VER_SUITE_SMALLBUSINESS,VER_SUITE_ENTERPRISE,VER_SUITE_BACKOFFICE,VER_SUITE_COMMUNICATIONS,VER_SUITE_TERMINAL,VER_SUITE_SMALLBUSINESS_RESTRICTED,VER_SUITE_EMBEDDEDNT和VER_SUITE_DATACENTER)
BYTE wProductType; // 用于標(biāo)識(shí)安裝了下面的哪個(gè)操作系統(tǒng):VER_NT_WORKSTATION,VER_NT_SERVER或VER_NT_DOMAIN_CONTROLLER
BYTE wReserved; // 留作將來使用
} OSVERSIONINFOEXA, *POSVERSIONINFOEXA, *LPOSVERSIONINFOEXA;
這個(gè)是擴(kuò)展版本。
4.2 CreateProcess函數(shù)
終于看到正題了~

BOOL CreateProcess(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
- 進(jìn)程啟動(dòng)時(shí),首先創(chuàng)建一個(gè)進(jìn)程內(nèi)核對(duì)象,該內(nèi)核對(duì)象不是進(jìn)程,是一個(gè)管理進(jìn)程存儲(chǔ)進(jìn)程信息的小型數(shù)據(jù)結(jié)構(gòu)。(計(jì)數(shù)為1)
- 創(chuàng)建一個(gè)虛擬地址空間,加載可執(zhí)行文件和DLL。
- 為進(jìn)程創(chuàng)建個(gè)主線程的內(nèi)核對(duì)象,和進(jìn)程內(nèi)核對(duì)象一樣,是用來管理和存儲(chǔ)線程信息的小型數(shù)據(jù)結(jié)構(gòu)。(計(jì)數(shù)為1)
- 調(diào)用C/C++運(yùn)行期啟動(dòng)代碼,主線程開始運(yùn)行,最終調(diào)用啟動(dòng)函數(shù),成功返回TRUE(未能正確加載DLL也返回TRUE,所以父進(jìn)程無法查看)。
4.2.1 pszApplicationName和pszCommandLine
pszCommandLine參數(shù):用來創(chuàng)建進(jìn)程的命令行參數(shù)。查看第一個(gè)標(biāo)記,如果沒有”.exe”會(huì)自動(dòng)添加”.exe”上去。(如果pszApplicationName參數(shù)NULL)
- 包含調(diào)用進(jìn)程的”.exe”文件的目錄。
- 調(diào)用進(jìn)程的當(dāng)前目錄。
- windows系統(tǒng)目錄
- windows目錄
- PATH環(huán)境變量中列出的目錄。
如果pszApplicationName參數(shù)不為NULL,系統(tǒng)將在當(dāng)前目錄中查找.exe文件(不會(huì)自動(dòng)添加“.exe”),如果找不到將失敗,此時(shí)pszCommandLine作為參數(shù)傳遞給可執(zhí)行程序的進(jìn)程。
4.2.2 psaProcess,psaThread和binHeritHandles
psaProcess,psaThread是進(jìn)程和進(jìn)程主線程內(nèi)核對(duì)象的安全屬性。默認(rèn)值為NULL。
binHeritHandles設(shè)置為TRUE表示父進(jìn)程在創(chuàng)建子進(jìn)程可以繼承安全屬性標(biāo)志里設(shè)置為TRUE的任何可繼承的內(nèi)核對(duì)象。如果設(shè)置為FALSE子進(jìn)程將不繼承任何內(nèi)核對(duì)象。
4.2.3 fdwCreate
用于標(biāo)識(shí)標(biāo)志,定義規(guī)則如何創(chuàng)建新進(jìn)程。我一般寫默認(rèn)值NULL。具體的太多了,請(qǐng)查看MSDN吧,不想寫了。
4.2.4 pvEnvironment
設(shè)置子進(jìn)程使用的環(huán)境內(nèi)存塊,一般默認(rèn)值為NULL,表示子進(jìn)程繼承父進(jìn)程的環(huán)境塊。
PVOID GetEnvironmentString(); // 獲取當(dāng)前內(nèi)存塊的地址
BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock); // 不用的時(shí)候調(diào)用此函數(shù)釋放內(nèi)存塊
4.2.5 pszCurDir
設(shè)定工作目錄和驅(qū)動(dòng)器號(hào),如果為NULL則和應(yīng)用程序的目錄相同,如果設(shè)置比如以’\0’結(jié)尾的包含驅(qū)動(dòng)器名的路徑。
4.2.6 psiStartInfo
typedef struct _STARTUPINFO {
DWORD cb; //(兩者兼有,控制臺(tái)和窗口程序)
LPSTR lpReserved; // (兩者兼有)保留,必須初始化為NULL
LPSTR lpDesktop; // (兩者兼有)用于標(biāo)識(shí)啟動(dòng)應(yīng)用程序所在的桌面的名字。如果該桌面存在,新進(jìn)程便與指定的桌面相關(guān)聯(lián)。如果桌面不存在,便創(chuàng)建一個(gè)帶有默認(rèn)屬性的桌面,并使用為新進(jìn)程指定的名字。如果lpDesktop是NULL(這是最常見的情況),那么該進(jìn)程將與當(dāng)前桌面相關(guān)聯(lián)。
LPSTR lpTitle; // (控制臺(tái))用于設(shè)定控制臺(tái)窗口名稱。如果lpTitle是NULL,則可執(zhí)行文件的名字將用作窗口名
DWORD dwX; // x,y坐標(biāo),只有當(dāng)子進(jìn)程用CW_USEDEFAULT作為CreateWindows的x參數(shù)來創(chuàng)建它的第一個(gè)重疊窗口時(shí),才使用這兩個(gè)坐標(biāo)。若是創(chuàng)建控制臺(tái)窗口的應(yīng)用程序,這些成員用于指明控制臺(tái)窗口的左上角。
DWORD dwY;
DWORD dwXSize; //(兩者兼有)設(shè)定窗口寬度和長(zhǎng)度,只有子進(jìn)程用WM_USEDEFAULT作為CreateWIndows的nWidth參數(shù)來創(chuàng)建它的第一個(gè)重疊窗口時(shí)才是用這個(gè)值??刂婆_(tái)就是控制臺(tái)的寬和長(zhǎng)
DWORD dwYSize;
DWORD dwXCountChars; //(控制臺(tái))用于設(shè)定子應(yīng)用程序控制臺(tái)的長(zhǎng)度和寬度(字符表示)
DWORD dwYCountChars;
DWORD dwFillAttribute;// (控制臺(tái))用于設(shè)定子應(yīng)用程序的控制臺(tái)背影顏色和文本。
DWORD dwFlags; // (兩者兼有)參見下一段
WORD wShowWindow; // (窗口)用于設(shè)定子應(yīng)用程序初次調(diào)用ShowWindow將SW_SHOWDEFAULT作為nCmdShow參數(shù)傳遞時(shí),該應(yīng)用程序的第一個(gè)重疊窗口應(yīng)該如何出現(xiàn)。本成員可以是通常用于ShowWindow函數(shù)的任何一個(gè)SW_*標(biāo)識(shí)符
WORD cbReserved2; // 保留,必須初始化為0
LPBYTE lpReserved2; // 保留,必須初始化為NULL
HANDLE hStdInput; // (控制臺(tái))用于設(shè)定控制臺(tái)輸入和輸出用的緩存的句柄。默認(rèn)設(shè)置hStdInput是鍵盤緩存,hStdOutput和hStdError窗口的緩存。
HANDLE hStdOutput; //
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
設(shè)置某些值,大部分需要默認(rèn)值,必須初始化為0都。
STARTUPINFO si = {sizeof(si)};
dwFlags標(biāo)志,用于修改如何來創(chuàng)建子進(jìn)程。
標(biāo)志
STARTF_USESIZE 使用dwXSize和dwYSize成員
STARTF_USESHOWWINDOW 使用wShowWIndow成員
STARTF_USEPOSITION 使用dwX和dwY成員
STARTF_USECOUNTCHARS 使用dwXCountChars和dwYCountChars成員
STARTF_USEFILLATTRIBUTE 使用dwFillAttribute成員
STARTF_USESTDHANDLES 使用hStdInput,hStdOutput和hStdError成員
STARTF_RUN_FULLSCREEN 強(qiáng)制再x86計(jì)算機(jī)上運(yùn)行的控制臺(tái)應(yīng)用程序以全屏幕方式啟動(dòng)運(yùn)行
STARTF_FORCEONFEEDBACK 光標(biāo)設(shè)置為沙漏,過了2秒如果進(jìn)程沒啟動(dòng)GUI,CreateProcess程序?qū)⒐鈽?biāo)設(shè)置為箭頭,5秒內(nèi)顯示一個(gè)窗口,成功調(diào)用GetMessage則反復(fù)箭頭,如果沒有成功,等待5秒 變?yōu)榧^
STARTF_FORCEOFFFEEDBACK
4.2.7 ppiProcInfo
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
返回的分別是,子進(jìn)程進(jìn)程句柄,子進(jìn)程中主線程的線程句柄,子進(jìn)程ID,子進(jìn)程中主線程的ID。
注意:
- hProcess和hThread被賦值后,內(nèi)核對(duì)象的計(jì)數(shù)器分別被+1。
- 系統(tǒng)為每個(gè)進(jìn)程和線程分配的ID值都是不同的,但是當(dāng)某進(jìn)程退出后,新進(jìn)程很可能會(huì)使用退出進(jìn)程的ID。
4.3 終止進(jìn)程的運(yùn)行
4.3.1 主線程的進(jìn)入點(diǎn)函數(shù)返回
最好強(qiáng)力推薦使用這種方式。
- 調(diào)用C++析構(gòu)函數(shù)。
- 釋放堆棧內(nèi)存。
- 將進(jìn)程退出代碼(進(jìn)程內(nèi)核對(duì)象中維護(hù))設(shè)置為進(jìn)入點(diǎn)函數(shù)返回值。
- 系統(tǒng)將進(jìn)程內(nèi)核對(duì)象的返回值減去1。
4.3.2 ExitProcess函數(shù)
避免使用這個(gè)方法。
VOID ExitProcess(UINT fuExitCode);
終止進(jìn)程運(yùn)行,并將退出碼設(shè)置為fuExitCode。
注意:
- 調(diào)用ExitProcess后,所有的代碼都將不會(huì)執(zhí)行,關(guān)閉進(jìn)程。
- 調(diào)用ExitProcess后,不會(huì)釋放C++析構(gòu)函數(shù)資源,有系統(tǒng)清理進(jìn)程時(shí)候直接釋放內(nèi)存。
- 進(jìn)程的主線程直接return退出后,啟動(dòng)函數(shù)中也會(huì)調(diào)用ExitProcess函數(shù),進(jìn)程終止,其他線程也會(huì)關(guān)閉。
- 進(jìn)程的主線程調(diào)用_endThreadex或者EndThread函數(shù)關(guān)閉主線程后,進(jìn)程沒有被關(guān)閉,子線程繼續(xù)運(yùn)行。
4.3.3 TerminiateProcess函數(shù)
能不用就別用。
BOOL TerminiateProcess(HANDLE hProcess, UINT fuExitCode;)
關(guān)閉指定為hProcess句柄的進(jìn)程,推出代碼為fuExitCode。
注意:
- 關(guān)閉進(jìn)程將丟失所有需要保存到硬盤的數(shù)據(jù),因?yàn)檫M(jìn)程關(guān)閉時(shí)候最后會(huì)自己釋放內(nèi)存資源,關(guān)閉內(nèi)核對(duì)象的句柄值,所以不會(huì)造成內(nèi)存泄露。
- 此函數(shù)是個(gè)異步函數(shù),它返回的時(shí)候無法知道,需要關(guān)閉的進(jìn)程是否已經(jīng)被強(qiáng)制關(guān)閉了。
4.3.4 進(jìn)程終止運(yùn)行時(shí)出現(xiàn)的情況
- 進(jìn)程中剩余的所有線程全部終止運(yùn)行。
- 釋放該進(jìn)程引用的GDI和用戶對(duì)象,內(nèi)核對(duì)象被關(guān)閉(別的進(jìn)程有引用則計(jì)數(shù)器減1,如果沒有引用則關(guān)閉內(nèi)核對(duì)象)。
- 推出代碼將從STILL_ACTIVE(后面章節(jié)線程將介紹該結(jié)構(gòu))改為傳遞給ExitProcess和TerminiateProcess代碼。
- 內(nèi)核對(duì)象狀態(tài)變?yōu)槭盏酵ㄖ獱顟B(tài)(線程中介紹),其他線程掛起,知道進(jìn)程終止。
- 進(jìn)程內(nèi)核對(duì)象計(jì)數(shù)減去1,或者關(guān)閉。
注意:
進(jìn)程內(nèi)核對(duì)象的壽命可能遠(yuǎn)遠(yuǎn)大于進(jìn)程本身,父進(jìn)程保留子進(jìn)程內(nèi)核對(duì)象可以查看它的推出代碼調(diào)用下面函數(shù):
BOOL GetExitCodeProcess(HANDLE hProcess, PDWORD pdwExitCode);
可以調(diào)用這個(gè)函數(shù)來判斷子進(jìn)程是否關(guān)閉,如果子進(jìn)程沒有關(guān)閉,它的STILL_ACTIVE標(biāo)識(shí)符定義為0x103。但是這么作效率不是很高。
4.4 子進(jìn)程
沒什么好說的,前面的都說了,用CloseHandle關(guān)閉子進(jìn)程和子進(jìn)程中主線程的句柄值來切斷父進(jìn)程和子進(jìn)程的所有聯(lián)系。
4.5 每局系統(tǒng)中運(yùn)行的進(jìn)程
利用ToolHelp函數(shù)族來開發(fā)管理操作系統(tǒng)上的進(jìn)程。打算自己也寫個(gè)試試。
本文章的內(nèi)容是本人學(xué)習(xí)Windows核心編程第四章后的總結(jié),有錯(cuò)誤請(qǐng)大家糾正,轉(zhuǎn)載注明出處:
http://www.cnblogs.com/xi52qian/
進(jìn)程由兩部分組成:
- 操作系統(tǒng)管理進(jìn)程的內(nèi)核對(duì)象。存放該進(jìn)程 的統(tǒng)計(jì)信息的地方。
- 地址空間,包含可執(zhí)行模塊和DLL模塊的代碼和數(shù)據(jù)。動(dòng)態(tài)分配的內(nèi)存(線程堆棧和堆)。
進(jìn)程是不活潑的,進(jìn)程當(dāng)中至少要有一個(gè)線程,每個(gè)線程要有自己的堆棧和自己的CPU寄存器。CPU通過算法給每個(gè)線程分配時(shí)間片的辦法來造成假象是在同時(shí)工作(多核通過自己的算法實(shí)現(xiàn)同時(shí)運(yùn)行)。
4.1 編寫第一個(gè)Windiws應(yīng)用程序
Windows兩種類型的程序:
- CUI程序,比如CMD.EXE等等。Microsoft Visual C++連接開關(guān)為/SUBSYSTEM:CONDOLE(程序啟動(dòng)時(shí)不能創(chuàng)建GUI程序)。
- GUI程序,圖形用戶程序,比如Notepad,Word等等。Microsoft Visual C++連接開關(guān)為/SUBSYSTEM:WINDOWS(程序啟動(dòng)時(shí)不能創(chuàng)建CUI程序)。
注意:倆者的概念其實(shí)是很模糊的,CUI可以創(chuàng)建GUI圖形界面,反之GUI程序可能用CUI程序。
Windows進(jìn)入點(diǎn)函數(shù)(區(qū)分在于CUI和GUI程序,ANSI碼和UNICODE碼)
int WINAPI WinMain(
HINSTANCE hinstExe,
HINSTANCE,
PSTR pszCmdLine,
int nCmdShow);
int WINAPI wWinMain(
HINSTANCE hinstExe,
HINSTANCE,
PWSTR pszCmdLine,
int nCmdShow);
int __cdecl main(
int argc,
char *argv[],
char *envp[]);
int __cdecl wmain(
int argc,
wchar_t *argv[],
wchar_t *envp[]);
其實(shí)Windows程序啟動(dòng)時(shí)最開始并不調(diào)用自己寫的入口函數(shù),而是調(diào)用系統(tǒng)的幾個(gè)入口函數(shù),以便可以調(diào)用malloc和free之類的函數(shù),初始化全局和靜態(tài)C++對(duì)象等。
- 檢索指向新進(jìn)程的完整命令行的指針。
- 檢索指向新進(jìn)程的環(huán)境變量的指針。
- 對(duì)C/C++運(yùn)行期的全局變量進(jìn)行初始化。如果包含StdLib.h文件,代碼就可以訪問這些變量。
- 對(duì)C運(yùn)行期的malloc和callo和其他底層輸入/輸出例程使用的內(nèi)存棧進(jìn)行初始化。
- 為所有全局和靜態(tài)C++類對(duì)象調(diào)用構(gòu)造函數(shù)。
應(yīng)用程序類型 進(jìn)入點(diǎn) 嵌入可執(zhí)行文件的啟動(dòng)函數(shù)
ANSI碼GUI應(yīng)用程序 WinMain WinMainCRTStattup
UNICODE碼GUI應(yīng)用程序 wWinMain wWinMainCRTStattup
ANSI碼CUI應(yīng)用程序 main mainCRTStattup
UNICODE碼CUI應(yīng)用程序 wmain wmainCRTStattup
注意:應(yīng)用程序會(huì)根據(jù)SUBSYSTEM開關(guān)來查找嵌入可執(zhí)行啟動(dòng)函數(shù),如果進(jìn)入點(diǎn)函數(shù)和啟動(dòng)函數(shù)不匹配則顯示鏈接錯(cuò)誤??梢詣h除SUBSYSTEM(VS Project Settings)開關(guān),這樣應(yīng)用程序會(huì)自動(dòng)需找匹配的函數(shù)。
進(jìn)入點(diǎn)函數(shù)返回時(shí)調(diào)用系統(tǒng)的exit函數(shù),將返回值傳遞給它。exit函數(shù)負(fù)責(zé)下面操作:
- 調(diào)用由_onexit函數(shù)的調(diào)用而注冊(cè)的任何函數(shù)。
- 為所有全局的和靜態(tài)的C++類對(duì)象調(diào)用析構(gòu)函數(shù)
- 調(diào)用操作系統(tǒng)的ExitProcess,并將返回值傳遞給他,關(guān)閉進(jìn)程。
4.1.1 進(jìn)程的實(shí)例句柄
WinMain/wWinMain函數(shù)的第一個(gè)參數(shù)表示進(jìn)程加載的可執(zhí)行文件的基地址/句柄。對(duì)于加載資源的調(diào)用都要使用此句柄,比如HICON LoadIcon(HINSTANCE, PCTSTR)。有的函數(shù)需要使用HMODULE,和HINSTANCE是一個(gè)意思(區(qū)分主要在于16位的操作系統(tǒng)中)。
HMODULE GetModuleHandle(PCTSTR pszModele);
函數(shù)作用,返回加載調(diào)用進(jìn)程中的可執(zhí)行文件或者DLL的基地址/句柄,參數(shù)是可執(zhí)行文件或者DLL的名稱。給pszModule賦值NULL,則返回的是進(jìn)程中可執(zhí)行文件的句柄。
注意:如果找不到則返回NULL。如果在DLL中傳遞NULL,返回的仍然是進(jìn)程加載的可執(zhí)行文件的句柄。
4.1.2 進(jìn)程的前一個(gè)實(shí)例句柄
第二個(gè)參數(shù)都傳遞NULL,是為16位系統(tǒng)所保留的。
4.1.3 進(jìn)程的命令行
注意:不要試圖修改命令行內(nèi)部?jī)?nèi)存的值,要使用修改先拷貝出來。
PTSTR GetCommandLine(); // 返回命令行字符串
PTSTR CommandLineToArgv(PTSTR pszCmdLine, int *pNumArgs); // 拆分命令行字符串函數(shù)
Demo:
int nNumargs;
PTSTR *ppArgv = CommandLineToArgv(GetCommandLine(), &nNumargs);
if ('x' == *ppArgv[1]) {
// TODO:
}
// 手動(dòng)釋放內(nèi)存,一般不需要釋放,系統(tǒng)會(huì)進(jìn)程關(guān)閉時(shí)候自動(dòng)釋放
HeapFree(GetProcessHeap, 0, ppArgv);
4.1.4 進(jìn)程的環(huán)境變量
環(huán)境塊是進(jìn)程地址空間中分配的內(nèi)存塊每個(gè)環(huán)境塊都包含一組字符串,格式如下:
VarName1=VarVarlue1\0
VarName2=VarVarlue2\0
VarName3=VarVarlue3\0
…..
VarNameX=VarVarlueX\0
\0
注意:
- 排序必須按照字母順序。
- ‘=’號(hào)不能是變量名的一部分。
- 等號(hào)左右兩邊的空格將被算做名稱或者值。
- 最后必須加個(gè)’\0’表示結(jié)束。
- 子進(jìn)程和父進(jìn)程不共用環(huán)境塊,修改不會(huì)影響父/子進(jìn)程。
DWORD GetEnvironmentVariable(PCTSTR pszName, PTSTR pszValue, DWORD cchValue);
pszName指變量名,pszValue指向變量值的緩存區(qū),cchValue緩存區(qū)的大小。找不到變量名或者設(shè)置的長(zhǎng)度不夠存放就返回0。
ExpandEnvironmentStrings(PCSTR pszSrc, PSTR pszDst, DWORD nSize);
用來用現(xiàn)實(shí)出可替換的環(huán)境變量的字符串。
BOOL SetEnvironmentVariable(PCTSTR pszName, PCTSTR pszValue);
設(shè)置環(huán)境變量的值,如果不存在則創(chuàng)建,如果存在則替換他的值。
4.1.5 進(jìn)程的親緣性
子進(jìn)程繼承父進(jìn)程的親緣性。(具體什么意思沒明白)
4.1.6 進(jìn)程的錯(cuò)誤模式
進(jìn)程可以設(shè)置如何處理一些錯(cuò)誤。
UINT SetErrorMode(UINT fuErrorMode);
各個(gè)模式用OR連接
標(biāo)志 說明
SEM_FAILCRITICALERRORS 系統(tǒng)不顯示關(guān)鍵錯(cuò)誤句柄消息框,并將錯(cuò)誤返回給調(diào)用進(jìn)程
SEM_NOGOFAULTERRORBOX 系統(tǒng)不顯示一般保護(hù)故障消息框。本標(biāo)志只應(yīng)該由采用異常情況處理程序來處理一般保護(hù)(GP)故障的調(diào)式應(yīng)用程式來設(shè)定
SEM_NOOPENFILEERRORBOX 當(dāng)系統(tǒng)找不到文件時(shí),它不顯示消息框。
SEM_NOALIGNMENTFAULTEXCEPT 系統(tǒng)自動(dòng)排除內(nèi)存沒有對(duì)其的故障,并使應(yīng)用程序看不到這些故障。本標(biāo)志對(duì)X86處理器不起作用。
子進(jìn)程繼承父進(jìn)程的錯(cuò)誤模式,如果不想讓子進(jìn)程繼承父進(jìn)程的錯(cuò)誤模式的話,可以再調(diào)用CreateProcess時(shí)設(shè)定CREATE_DEFAULT_ERROR_MODE標(biāo)志。
4.1.7 進(jìn)程的當(dāng)前驅(qū)動(dòng)器和目錄
默認(rèn)情況下不提供全路徑的話,系統(tǒng)就會(huì)在當(dāng)前驅(qū)動(dòng)器和目錄中查找文件,比如CreateFile,因?yàn)轵?qū)動(dòng)器和目錄是每個(gè)進(jìn)程來維護(hù)的,所以某個(gè)線程改變了目錄和驅(qū)動(dòng)器會(huì)改變整個(gè)進(jìn)程的目錄和驅(qū)動(dòng)器。
下面兩個(gè)函數(shù)讀取和設(shè)置:
DWORD GetCurrentDirectory(DWORD cchCurDir, PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
4.1.8 進(jìn)程的當(dāng)前目錄
驅(qū)動(dòng)器環(huán)境塊的格式:
=C:=C:\Utility\Bin
程序查找驅(qū)動(dòng)器環(huán)境塊,如果沒有則按驅(qū)動(dòng)器名查找。
子進(jìn)程不能繼承父進(jìn)程的驅(qū)動(dòng)器塊,如果想繼承必須寫到環(huán)境變量中去。(好像是這樣,如果有不對(duì)請(qǐng)高人指點(diǎn))。
DWORD GetFullPathName(PCTSTR pszFile, DWORD cchPath, PTSTR pszPath, PTSTR *ppszFilePart);
獲取驅(qū)動(dòng)器的當(dāng)前目錄,比如:
TCHAR szCurDir[MAX_PATH];
DWORD GetFullPathName(TEXT("C:"), MAX_PATH, szCurDir, NULL);
4.1.9 系統(tǒng)版本
DWORD GetVersion();此函數(shù)存在高地位的混論BUG,所以盡量不要使用。
BOOL GetVersion(POSVERSIONINFOEX pVersionInfomation);
typedef struct _OSVERSIONINFOEXA {
DWORD dwOSVersionInfoSize; // 在調(diào)用GetVersionEx函數(shù)之前,必必須置為sizeof(OSVERSIONINFOEX)
DWORD dwMajorVersion; // 主系統(tǒng)的主要版本號(hào)
DWORD dwMinorVersion; // 主系統(tǒng)的次要版本號(hào)
DWORD dwBuildNumber; // 當(dāng)前系統(tǒng)的構(gòu)建號(hào)
DWORD dwPlatformId; // 識(shí)別當(dāng)前系統(tǒng)的平臺(tái)??梢允筕ER_PLATFORM_WIN32(WIN32),VER_PLATFORM_WIN32_WINDOWS(WINDOWS 95/WINDOWS 98),VER_PLATFORM_WIN32_NT(WINDOWS NT/WINDOWS 2000)或VER_PLATFORM_WIN32_CEHH(WINDOWS CE)
CHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usage 本域包含了附加文本,用于提供關(guān)于已經(jīng)安裝的操作系統(tǒng)的詳細(xì)信息
WORD wServicePackMajor; // 最新安裝的服務(wù)程序包的主要版本號(hào)
WORD wServicePackMinor; // 最新安裝的服務(wù)程序包的次要版本號(hào)
WORD wSuiteMask; // 用于標(biāo)識(shí)系統(tǒng)上存在那個(gè)程序組(VER_SUITE_SMALLBUSINESS,VER_SUITE_ENTERPRISE,VER_SUITE_BACKOFFICE,VER_SUITE_COMMUNICATIONS,VER_SUITE_TERMINAL,VER_SUITE_SMALLBUSINESS_RESTRICTED,VER_SUITE_EMBEDDEDNT和VER_SUITE_DATACENTER)
BYTE wProductType; // 用于標(biāo)識(shí)安裝了下面的哪個(gè)操作系統(tǒng):VER_NT_WORKSTATION,VER_NT_SERVER或VER_NT_DOMAIN_CONTROLLER
BYTE wReserved; // 留作將來使用
} OSVERSIONINFOEXA, *POSVERSIONINFOEXA, *LPOSVERSIONINFOEXA;
這個(gè)是擴(kuò)展版本。
4.2 CreateProcess函數(shù)
終于看到正題了~

BOOL CreateProcess(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
- 進(jìn)程啟動(dòng)時(shí),首先創(chuàng)建一個(gè)進(jìn)程內(nèi)核對(duì)象,該內(nèi)核對(duì)象不是進(jìn)程,是一個(gè)管理進(jìn)程存儲(chǔ)進(jìn)程信息的小型數(shù)據(jù)結(jié)構(gòu)。(計(jì)數(shù)為1)
- 創(chuàng)建一個(gè)虛擬地址空間,加載可執(zhí)行文件和DLL。
- 為進(jìn)程創(chuàng)建個(gè)主線程的內(nèi)核對(duì)象,和進(jìn)程內(nèi)核對(duì)象一樣,是用來管理和存儲(chǔ)線程信息的小型數(shù)據(jù)結(jié)構(gòu)。(計(jì)數(shù)為1)
- 調(diào)用C/C++運(yùn)行期啟動(dòng)代碼,主線程開始運(yùn)行,最終調(diào)用啟動(dòng)函數(shù),成功返回TRUE(未能正確加載DLL也返回TRUE,所以父進(jìn)程無法查看)。
4.2.1 pszApplicationName和pszCommandLine
pszCommandLine參數(shù):用來創(chuàng)建進(jìn)程的命令行參數(shù)。查看第一個(gè)標(biāo)記,如果沒有”.exe”會(huì)自動(dòng)添加”.exe”上去。(如果pszApplicationName參數(shù)NULL)
- 包含調(diào)用進(jìn)程的”.exe”文件的目錄。
- 調(diào)用進(jìn)程的當(dāng)前目錄。
- windows系統(tǒng)目錄
- windows目錄
- PATH環(huán)境變量中列出的目錄。
如果pszApplicationName參數(shù)不為NULL,系統(tǒng)將在當(dāng)前目錄中查找.exe文件(不會(huì)自動(dòng)添加“.exe”),如果找不到將失敗,此時(shí)pszCommandLine作為參數(shù)傳遞給可執(zhí)行程序的進(jìn)程。
4.2.2 psaProcess,psaThread和binHeritHandles
psaProcess,psaThread是進(jìn)程和進(jìn)程主線程內(nèi)核對(duì)象的安全屬性。默認(rèn)值為NULL。
binHeritHandles設(shè)置為TRUE表示父進(jìn)程在創(chuàng)建子進(jìn)程可以繼承安全屬性標(biāo)志里設(shè)置為TRUE的任何可繼承的內(nèi)核對(duì)象。如果設(shè)置為FALSE子進(jìn)程將不繼承任何內(nèi)核對(duì)象。
4.2.3 fdwCreate
用于標(biāo)識(shí)標(biāo)志,定義規(guī)則如何創(chuàng)建新進(jìn)程。我一般寫默認(rèn)值NULL。具體的太多了,請(qǐng)查看MSDN吧,不想寫了。
4.2.4 pvEnvironment
設(shè)置子進(jìn)程使用的環(huán)境內(nèi)存塊,一般默認(rèn)值為NULL,表示子進(jìn)程繼承父進(jìn)程的環(huán)境塊。
PVOID GetEnvironmentString(); // 獲取當(dāng)前內(nèi)存塊的地址
BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock); // 不用的時(shí)候調(diào)用此函數(shù)釋放內(nèi)存塊
4.2.5 pszCurDir
設(shè)定工作目錄和驅(qū)動(dòng)器號(hào),如果為NULL則和應(yīng)用程序的目錄相同,如果設(shè)置比如以’\0’結(jié)尾的包含驅(qū)動(dòng)器名的路徑。
4.2.6 psiStartInfo
typedef struct _STARTUPINFO {
DWORD cb; //(兩者兼有,控制臺(tái)和窗口程序)
LPSTR lpReserved; // (兩者兼有)保留,必須初始化為NULL
LPSTR lpDesktop; // (兩者兼有)用于標(biāo)識(shí)啟動(dòng)應(yīng)用程序所在的桌面的名字。如果該桌面存在,新進(jìn)程便與指定的桌面相關(guān)聯(lián)。如果桌面不存在,便創(chuàng)建一個(gè)帶有默認(rèn)屬性的桌面,并使用為新進(jìn)程指定的名字。如果lpDesktop是NULL(這是最常見的情況),那么該進(jìn)程將與當(dāng)前桌面相關(guān)聯(lián)。
LPSTR lpTitle; // (控制臺(tái))用于設(shè)定控制臺(tái)窗口名稱。如果lpTitle是NULL,則可執(zhí)行文件的名字將用作窗口名
DWORD dwX; // x,y坐標(biāo),只有當(dāng)子進(jìn)程用CW_USEDEFAULT作為CreateWindows的x參數(shù)來創(chuàng)建它的第一個(gè)重疊窗口時(shí),才使用這兩個(gè)坐標(biāo)。若是創(chuàng)建控制臺(tái)窗口的應(yīng)用程序,這些成員用于指明控制臺(tái)窗口的左上角。
DWORD dwY;
DWORD dwXSize; //(兩者兼有)設(shè)定窗口寬度和長(zhǎng)度,只有子進(jìn)程用WM_USEDEFAULT作為CreateWIndows的nWidth參數(shù)來創(chuàng)建它的第一個(gè)重疊窗口時(shí)才是用這個(gè)值。控制臺(tái)就是控制臺(tái)的寬和長(zhǎng)
DWORD dwYSize;
DWORD dwXCountChars; //(控制臺(tái))用于設(shè)定子應(yīng)用程序控制臺(tái)的長(zhǎng)度和寬度(字符表示)
DWORD dwYCountChars;
DWORD dwFillAttribute;// (控制臺(tái))用于設(shè)定子應(yīng)用程序的控制臺(tái)背影顏色和文本。
DWORD dwFlags; // (兩者兼有)參見下一段
WORD wShowWindow; // (窗口)用于設(shè)定子應(yīng)用程序初次調(diào)用ShowWindow將SW_SHOWDEFAULT作為nCmdShow參數(shù)傳遞時(shí),該應(yīng)用程序的第一個(gè)重疊窗口應(yīng)該如何出現(xiàn)。本成員可以是通常用于ShowWindow函數(shù)的任何一個(gè)SW_*標(biāo)識(shí)符
WORD cbReserved2; // 保留,必須初始化為0
LPBYTE lpReserved2; // 保留,必須初始化為NULL
HANDLE hStdInput; // (控制臺(tái))用于設(shè)定控制臺(tái)輸入和輸出用的緩存的句柄。默認(rèn)設(shè)置hStdInput是鍵盤緩存,hStdOutput和hStdError窗口的緩存。
HANDLE hStdOutput; //
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
設(shè)置某些值,大部分需要默認(rèn)值,必須初始化為0都。
STARTUPINFO si = {sizeof(si)};
dwFlags標(biāo)志,用于修改如何來創(chuàng)建子進(jìn)程。
標(biāo)志
STARTF_USESIZE 使用dwXSize和dwYSize成員
STARTF_USESHOWWINDOW 使用wShowWIndow成員
STARTF_USEPOSITION 使用dwX和dwY成員
STARTF_USECOUNTCHARS 使用dwXCountChars和dwYCountChars成員
STARTF_USEFILLATTRIBUTE 使用dwFillAttribute成員
STARTF_USESTDHANDLES 使用hStdInput,hStdOutput和hStdError成員
STARTF_RUN_FULLSCREEN 強(qiáng)制再x86計(jì)算機(jī)上運(yùn)行的控制臺(tái)應(yīng)用程序以全屏幕方式啟動(dòng)運(yùn)行
STARTF_FORCEONFEEDBACK 光標(biāo)設(shè)置為沙漏,過了2秒如果進(jìn)程沒啟動(dòng)GUI,CreateProcess程序?qū)⒐鈽?biāo)設(shè)置為箭頭,5秒內(nèi)顯示一個(gè)窗口,成功調(diào)用GetMessage則反復(fù)箭頭,如果沒有成功,等待5秒 變?yōu)榧^
STARTF_FORCEOFFFEEDBACK
4.2.7 ppiProcInfo
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
返回的分別是,子進(jìn)程進(jìn)程句柄,子進(jìn)程中主線程的線程句柄,子進(jìn)程ID,子進(jìn)程中主線程的ID。
注意:
- hProcess和hThread被賦值后,內(nèi)核對(duì)象的計(jì)數(shù)器分別被+1。
- 系統(tǒng)為每個(gè)進(jìn)程和線程分配的ID值都是不同的,但是當(dāng)某進(jìn)程退出后,新進(jìn)程很可能會(huì)使用退出進(jìn)程的ID。
4.3 終止進(jìn)程的運(yùn)行
4.3.1 主線程的進(jìn)入點(diǎn)函數(shù)返回
最好強(qiáng)力推薦使用這種方式。
- 調(diào)用C++析構(gòu)函數(shù)。
- 釋放堆棧內(nèi)存。
- 將進(jìn)程退出代碼(進(jìn)程內(nèi)核對(duì)象中維護(hù))設(shè)置為進(jìn)入點(diǎn)函數(shù)返回值。
- 系統(tǒng)將進(jìn)程內(nèi)核對(duì)象的返回值減去1。
4.3.2 ExitProcess函數(shù)
避免使用這個(gè)方法。
VOID ExitProcess(UINT fuExitCode);
終止進(jìn)程運(yùn)行,并將退出碼設(shè)置為fuExitCode。
注意:
- 調(diào)用ExitProcess后,所有的代碼都將不會(huì)執(zhí)行,關(guān)閉進(jìn)程。
- 調(diào)用ExitProcess后,不會(huì)釋放C++析構(gòu)函數(shù)資源,有系統(tǒng)清理進(jìn)程時(shí)候直接釋放內(nèi)存。
- 進(jìn)程的主線程直接return退出后,啟動(dòng)函數(shù)中也會(huì)調(diào)用ExitProcess函數(shù),進(jìn)程終止,其他線程也會(huì)關(guān)閉。
- 進(jìn)程的主線程調(diào)用_endThreadex或者EndThread函數(shù)關(guān)閉主線程后,進(jìn)程沒有被關(guān)閉,子線程繼續(xù)運(yùn)行。
4.3.3 TerminiateProcess函數(shù)
能不用就別用。
BOOL TerminiateProcess(HANDLE hProcess, UINT fuExitCode;)
關(guān)閉指定為hProcess句柄的進(jìn)程,推出代碼為fuExitCode。
注意:
- 關(guān)閉進(jìn)程將丟失所有需要保存到硬盤的數(shù)據(jù),因?yàn)檫M(jìn)程關(guān)閉時(shí)候最后會(huì)自己釋放內(nèi)存資源,關(guān)閉內(nèi)核對(duì)象的句柄值,所以不會(huì)造成內(nèi)存泄露。
- 此函數(shù)是個(gè)異步函數(shù),它返回的時(shí)候無法知道,需要關(guān)閉的進(jìn)程是否已經(jīng)被強(qiáng)制關(guān)閉了。
4.3.4 進(jìn)程終止運(yùn)行時(shí)出現(xiàn)的情況
- 進(jìn)程中剩余的所有線程全部終止運(yùn)行。
- 釋放該進(jìn)程引用的GDI和用戶對(duì)象,內(nèi)核對(duì)象被關(guān)閉(別的進(jìn)程有引用則計(jì)數(shù)器減1,如果沒有引用則關(guān)閉內(nèi)核對(duì)象)。
- 推出代碼將從STILL_ACTIVE(后面章節(jié)線程將介紹該結(jié)構(gòu))改為傳遞給ExitProcess和TerminiateProcess代碼。
- 內(nèi)核對(duì)象狀態(tài)變?yōu)槭盏酵ㄖ獱顟B(tài)(線程中介紹),其他線程掛起,知道進(jìn)程終止。
- 進(jìn)程內(nèi)核對(duì)象計(jì)數(shù)減去1,或者關(guān)閉。
注意:
進(jìn)程內(nèi)核對(duì)象的壽命可能遠(yuǎn)遠(yuǎn)大于進(jìn)程本身,父進(jìn)程保留子進(jìn)程內(nèi)核對(duì)象可以查看它的推出代碼調(diào)用下面函數(shù):
BOOL GetExitCodeProcess(HANDLE hProcess, PDWORD pdwExitCode);
可以調(diào)用這個(gè)函數(shù)來判斷子進(jìn)程是否關(guān)閉,如果子進(jìn)程沒有關(guān)閉,它的STILL_ACTIVE標(biāo)識(shí)符定義為0x103。但是這么作效率不是很高。
4.4 子進(jìn)程
沒什么好說的,前面的都說了,用CloseHandle關(guān)閉子進(jìn)程和子進(jìn)程中主線程的句柄值來切斷父進(jìn)程和子進(jìn)程的所有聯(lián)系。
4.5 每局系統(tǒng)中運(yùn)行的進(jìn)程
利用ToolHelp函數(shù)族來開發(fā)管理操作系統(tǒng)上的進(jìn)程。打算自己也寫個(gè)試試。
本文章的內(nèi)容是本人學(xué)習(xí)Windows核心編程第四章后的總結(jié),有錯(cuò)誤請(qǐng)大家糾正,轉(zhuǎn)載注明出處:
http://www.cnblogs.com/xi52qian/
頭文件 <iostream>
一. 對(duì)終端的操作
相關(guān)頭文件#include <iostream>
1. 輸入istream
2. 輸出ostream
3. iostream繼承istream和ostream 所以它具有輸入輸出功能。
為了方便這個(gè)庫定義了下列三個(gè)標(biāo)準(zhǔn)流對(duì)象:
1. cin 代表標(biāo)準(zhǔn)輸入istream類對(duì)象一般地cin使我們能夠從用戶終端讀入數(shù)據(jù)。
2. cout 代表標(biāo)準(zhǔn)輸出ostream類對(duì)象一般地cout使我們能夠向用戶終端寫數(shù)據(jù)。
3. cerr 代表標(biāo)準(zhǔn)錯(cuò)誤ostream類對(duì)象一般地cerr是導(dǎo)出程序錯(cuò)誤消息的地方。
另外,輸出主要由重載的左移操作符<< 來完成類似地輸入主要由重載的右移操作符>>來完成。
Demo1:
#include <iostream>
#include <string>
int main() {
string in_string;
// 向用戶終端寫字符串
std::cout << "Please enter your name: ";
// 把用戶輸入的讀取到 in_string 中
std::cin >> in_string;
if ( in_string.empty() )
// 產(chǎn)生一個(gè)錯(cuò)誤消息輸出到用戶終端
std::cerr << "error: input string is empty!\n";
else std::cout << "hello, " << in_string << "!\n";
}
二. 對(duì)文件的操作
相關(guān)頭#include <fstream>
1. ifstream 從istream 派生把一個(gè)文件綁到程序上從文件讀取數(shù)據(jù)用來輸入。
2. ofstream 從ostream 派生把一個(gè)文件綁到程序上用來向文件寫入數(shù)據(jù)。
3. fstream 從iostream 派生把一個(gè)文件綁到程序上用來輸入和輸出。
注:由于在fstream 頭文件中也包含了iostream 頭文件所以我們不需要同時(shí)包含這兩個(gè)文
件C++對(duì)于文件的輸入輸出也支持同樣的輸入和輸出操作符。
Demo2:
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
int main()
{
string ifile;
cout << "Please enter file to sort: ";
cin >> ifile;
// 構(gòu)造一個(gè) ifstream 輸入文件對(duì)象
ifstream infile( ifile.c_str() );
if( ! infile ) {
cerr << "error: unable to open input file: " << ifile << endl;
return -1;
}
string ofile = ifile + ".sort";
// 構(gòu)造一個(gè) ofstream 輸出文件對(duì)象
ofstream outfile( ofile.c_str() );
if( !outfile ) {
cerr << "error: unable to open output file: " << ofile << endl;
return -2;
}
string buffer;
vector<string> text;
int cnt = 1;
while ( infile >> buffer ) {
text.push_back( buffer );
cout << buffer << ( cnt++ % 8 ? " " : "\n" );
}
sort( text.begin(), text.end() );
// ok: 把排序后的詞打印到 outfile
vector<string>::iterator iter = text.begin();
for ( cnt = 1; iter != text.end(); ++iter, ++cnt )
outfile << *iter << (cnt%8 ? " " : "\n" );
return 0;
}
三. 對(duì)字符流操作
相關(guān)的頭文件#include <sstream>
1 istringstream 從istream 派生從一個(gè)字符串中讀取數(shù)據(jù)。
2 ostringstream 從ostream 派生寫入到一個(gè)字符串中。
3 stringstream 從iostream 派生從字符串中讀取或者寫入到字符串中。
Demo3:
#include <sstream>
string program_name( "our_program" );
string version( "0.01" );
// ...
string mumble( int *array, int size )
{
if ( ! array ) {
ostringstream out_message;
out_message << "error: "
<< program_name << "--" << version
<< ": " << __FILE__ << ": " << __LINE__
<<" -- ptr is set to 0; "
<< " must address some array.\n";
// 返回底層 string 對(duì)象
return out_message.str();
}
四. wchar_t型
支持wchar_t類型的流讀寫操作
wcin wcout wcerr wiostream 等等....
20.1 輸出操作符<<
1. 支持所有的內(nèi)置數(shù)據(jù)類型 包括string ,const char* 和complex。以及函數(shù)的調(diào)用。
2. endl等價(jià)于輸出換行操作符,然后再刷新緩存區(qū)。cout << '\n' << flush;
3. <<可以連接使用,因?yàn)閛perator<<的返回值是ostream&。
4. cout << p << &i << endl;會(huì)輸出指針的地址(p是int *,i是int)。如果是const char * pcStr不會(huì)輸出指針地址值,
輸出的是字符串。轉(zhuǎn)換為cout << static_cast<void *>(const_cast<char *>(pcStr));
5. <<運(yùn)算符的優(yōu)先級(jí)高于?:所以和?:使用時(shí)注意加括號(hào)。
6. ostream_iterator和cout的使用。
Demo4:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
string pooh_pals[] = {
"Tigger", "Piglet", "Eeyore", "Rabbit"
};
int main()
{
vector<string> ppals( pooh_pals, pooh_pals+4 );
vector<string>::iterator iter = ppals.begin();
vector<string>::iterator iter_end = ppals.end();
cout << "These are Pooh's pals: ";
// 把每個(gè)元素拷貝到 cout ...
ostream_iterator< string > output( cout, " " );
copy( iter, iter_end, output );
cout << endl;
}
7. cout << 遇到‘\0’會(huì)定制輸出。
20.2 輸入操作符>>
1. while(cin >> 變量)的時(shí)候有兩種情況會(huì)讓循環(huán)停止(false):
(1) 讀到文件結(jié)束在這種情況下我們已經(jīng)正確地讀完文件中所有的值。
(2) 遇到一個(gè)無效的值比如3.14159小數(shù)點(diǎn)是非法的1e-1字符文字e是非法的或者一般的任意字符串文字在讀入一個(gè)無效值的
情況下istream對(duì)象被放置到一種錯(cuò)誤的狀態(tài)中并且對(duì)于值的所有讀入動(dòng)作都將停止。
2. 預(yù)定義的輸入操作符可以接受任何的內(nèi)置數(shù)據(jù)類型包括C 風(fēng)格字符由以及標(biāo)準(zhǔn)庫string和complex 類類型。
3. 缺省情況下輸入操作符丟棄任何中間空白空格制表符換行符走紙以及回車。
Demo5:
#include <iostream>
#include <string>
int main()
{
int item_number;
string item_name;
double item_price;
cout << "Please enter the item_number, item_name, and price: " << endl;
cin >> item_number;
cin >> item_name;
cin >> item_price;
cout << "The values entered are: item# "
<< item_number << " "
<< item_name << " @$"
<< item_price << endl;
}
4. 如果希望讀入空白符號(hào)可以利用cin.get()方法一個(gè)個(gè)讀入,或者設(shè)置skipws和noskipws選項(xiàng)(后面介紹)。
5. isream_iterator和cin關(guān)聯(lián)使用。
Demo6:
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
int main()
{
istream_iterator< string > in( cin ), eos ;
vector< string > text ;
// 從標(biāo)準(zhǔn)輸入向 text 拷貝值
copy( in , eos , back_inserter( text ) ) ;
sort( text.begin() , text.end() ) ;
// 刪除所有重復(fù)的值
vector< string >::iterator it ;
it = unique( text.begin() , text.end() ) ;
text.erase( it , text.end() ) ;
// 顯示結(jié)果 vector
int line_cnt = 1 ;
for ( vector< string >::iterator iter = text.begin();
iter != text.end() ; ++iter , ++line_cnt )
cout << *iter
<< ( line_cnt % 9 ? " " : "\n" ) ;
cout << endl;
}
20.2.1 字符串輸入
1. setw函數(shù):防止讀入字符串益處(char[4],輸入4個(gè)以上字節(jié)),while ( cin >> setw( bufSize ) >> buf )
這里bufSize是字符數(shù)組buf的長(zhǎng)度setw()把長(zhǎng)度等于或大于bufSize 的字符串分成最大長(zhǎng)度為bufSize - 1的兩個(gè)
或多個(gè)字符串,在每個(gè)新串的末尾放一個(gè)空字符為了使用setw()要求程序包含iomanip頭文件#include <iomanip>。
2. 用string沒有char *那些長(zhǎng)度和內(nèi)存溢出問題,更容易使用。
20.3 其他輸入輸出操作符
一. istream的成員get()一次讀入一個(gè)字節(jié)。屬于istream
1. get(char& ch)從輸入流中提取一個(gè)字符包括空白字符并將它存儲(chǔ)在ch中它返回被應(yīng)用的istream對(duì)象。
與之對(duì)應(yīng)的是ostream的put()方法,每次讀入一個(gè)字節(jié),然后返回ostream。
2. get()的第二個(gè)版本也從輸入流讀入一個(gè)字符區(qū)別是它返回該字符值而不是被應(yīng)用的istream 對(duì)象它的返回類型
是int 而不是char 因?yàn)樗卜祷匚募驳臉?biāo)志end-of-file該標(biāo)志通常用-1 來表示以便與字符集區(qū)分開為測(cè)試返回
值是否為文件尾我們將它與iostream 頭文件中定義的常量EOF 做比較。
Demo7:
#include <iostream>
int main()
{
int ch;
// 或使用:
// while (( ch = cin.get() ) && ch != EOF)
while (( ch = cin.get()) != EOF)
cout.put(ch);
return 0;
}
3. get(char *sink, streamsize size, char delimiter='\n')
(1) sink 代表一個(gè)字符數(shù)組用來存放被讀取到的字符。
(2) size 代表可以從istream 中讀入的字符的最大數(shù)目。
(3) delimiter 表示如果遇到它就結(jié)束讀取字符的動(dòng)作delimiter 字符本身不會(huì)被讀入而是留在istream 中作為istream 的
下一個(gè)字符一種常見的錯(cuò)誤是在執(zhí)行第二個(gè)get()之前忘了去掉delimiter。
二. ignore( streamsize length = 1, int delim = traits::eof )函數(shù): 屬于istream
我們用istream 成員函數(shù)ignore()來去掉delimiter
缺省情況下?lián)Q行符被用作delimiter。
三. gcount()函數(shù): 屬于istream
它返回由最后的get()或getline()調(diào)用實(shí)際提取的字符數(shù)。
Demo8:
#include <iostream>
int main()
{
const int max_line = 1024;
char line[ max_line ];
while ( cin.get( line, max_line ))
{
// 最大讀取數(shù)量 max_line - 1, 也可以為 null
int get_count = cin.gcount();
cout << "characters actually read: "
<< get_count << endl;
// 處理每一行
// 如果遇到換行符
// 在讀下一行之前去掉它
if ( get_count & max_line-1 )
cin.ignore();
}
}
四. getline(char *sink, streamsize size, char delimiter='\n')屬于istream
五. write( const char *sink, streamsize length )屬于ostream
提供了另外一種方法可以輸出字符數(shù)組”它不是輸出直到終止空字符為止的所有字符“而是輸出某個(gè)長(zhǎng)度的字符序列包括內(nèi)含的
空字符它的函數(shù)。length 指定要顯示的字符個(gè)數(shù)write()返回當(dāng)前被調(diào)用的ostream 類對(duì)象。
六. read( char* addr, streamsize size )屬于istream
read()從輸入流中提取size 個(gè)連續(xù)的字節(jié)并將其放在地址從addr 開始的內(nèi)存中g(shù)count()返回由最后一個(gè)read()調(diào)用提取的字
節(jié)數(shù)而read()返回當(dāng)前被調(diào)用的istream 類對(duì)象。
Demo9:
#include <iostream>
int main()
{
const lineSize = 1024;
int lcnt = 0; // 讀入多少行
int max = -1; // 最長(zhǎng)行的長(zhǎng)度
char inBuf[ lineSize ];
// 讀取 1024 個(gè)字符或者遇到換行符
while (cin.getline( inBuf, lineSize ))
{
// 實(shí)際讀入多少字符
int readin = cin.gcount();
// 統(tǒng)計(jì): 行數(shù)最長(zhǎng)行
++lcnt;
if ( readin > max )
max = readin;
cout << "Line #" << lcnt
<< "\tChars read: " << readin << endl;
cout.write( inBuf, readin).put('\n').put('\n');
}
cout << "Total lines read: " << lcnt << endl;
cout << "Longest line read: " << max << endl;
}
七. getline( istream &is, string str, char delimiter );
這個(gè)getline()實(shí)例的行為如下讀入最大數(shù)目為str::max_size-1 個(gè)字符如果輸入序列超出這個(gè)限制則讀操作失敗
并且istream 對(duì)象被設(shè)置為錯(cuò)誤狀態(tài)否則當(dāng)讀到delimiter 它被從istream 中丟棄但沒有被插入到string 中或遇
到文件結(jié)束符時(shí)輸入結(jié)束。
八. putback( char c ); 將字符放回 iostream。
九. unget();往回重置下一個(gè) istream 項(xiàng)。
十. peek(); 返回下一個(gè)字符或 EOF,但不要提取出來。
Demo10:
char ch, next, lookahead;
while ( cin.get( ch ))
{
switch (ch) {
case '/':
// 是注釋行嗎? 用 peek() 看一看:
// 是的? ignore() 余下的行
next = cin.peek();
if ( next == '/' )
cin.ignore( lineSize, '\n' );
break;
case '>':
// 查找 >>=
next = cin.peek();
if ( next == '>' ) {
lookahead = cin.get();
next = cin.peek();
if ( next != '=' )
cin.putback( lookahead );
}
20.4 重載輸出操作符<<
輸出操作符是一個(gè)雙目操作符它返回一個(gè)ostream 引用重載定義的通用框架如下
// 重載 output 操作符的通用框架
ostream&
operator <<( ostream& os, const ClassType &object )
{
// 準(zhǔn)備對(duì)象的特定邏輯
// 成員的實(shí)際輸出
os << // ...
// 返回 ostream 對(duì)象
return os;
}
注:因?yàn)榈谝粋€(gè)實(shí)參是一個(gè)ostream 引用所以輸出操作符必須定義為非成員函數(shù),當(dāng)輸出操作符要求訪問非公有成員
時(shí)必須將它聲明為該類的友元。
20.5 重載輸入操作符>>
1 由于不正確的格式而導(dǎo)致失敗istream 應(yīng)該把狀態(tài)標(biāo)記為fail。setstate( ios_base::failbit )。
2 對(duì)于錯(cuò)誤狀態(tài)中的iostream 插入和提取操作沒有影響。
Demo11:
#include <iostream>
#include "WordCount.h"
/* 必須修改 WordCount, 指定輸入操作符為友元
class WordCount {
friend ostream& operator<<( ostream&, const WordCount& );
friend istream& operator>>( istream&, WordCount& );
*/
istream&
operator>>( istream &is, WordCount &wd )
{
/* WordCount 對(duì)象被讀入的格式:
* <2> string
* <7,3> <12,36>
*/
int ch;
/* 讀入小于符號(hào), 如果不存在
* 則設(shè)置 istream 為失敗狀態(tài)并退出
*/
if ((ch = is.get()) != '<' )
{
is.setstate( ios_base::failbit );
return is;
}
// 讀入多少個(gè)
int occurs;
is >> occurs;
// 取 >; 不檢查錯(cuò)誤
while ( is && (ch = is.get()) != '>' );
is >> wd._word;
// 讀入位置
// 每個(gè)位置的格式: < line, col >
for ( int ix = 0; ix < occurs; ++ix )
{
int line, col;
// 提取值
while (is && (ch = is.get())!= '<' );
is >> line;
while (is && (ch = is.get())!= ',' );
is >> col;
while (is && (ch = is.get())!= '>' );
wd.occurList.push_back( Location( line, col ));
}
return is;
}
20.6 文件輸入和輸出
包含頭文件#include <fstream>
1. ofstream的構(gòu)造函數(shù)要制定打開模式
輸出模式ios_base::out 或附加模式ios_base::app 等等。在缺省情況下ostream文件以輸出模式打開。
注:如果在輸出模式下打開已經(jīng)存在的文件則所有存儲(chǔ)在該文件中的數(shù)據(jù)都將被丟棄如果我們希望增加而不是替換現(xiàn)
有文件中的數(shù)據(jù)則應(yīng)該以附加模式打開文件于是新寫到文件中的數(shù)據(jù)將添加到文件尾部在這兩種模式下如果文件不存
在程序都會(huì)創(chuàng)建一個(gè)新文件。
2. 判斷是否打開if (!outFile /*ofstream對(duì)象*/)
3. 因?yàn)閛fstream派生于ostream,所以ofstream擁有ostream的操作,比如put()等等。
4. 自定義<<操作輸入到ofstream中。
5. 用ifstream讀入一個(gè)文件,派生于istream,所有擁有istream的操作,比如get()等等。
6. close()函數(shù)斷開和文件的關(guān)聯(lián)。
7. fstream派生于iostream,它具有對(duì)文件的讀寫操作。
8. seekg和seekp標(biāo)記當(dāng)前位置,g是在讀文件中使用,p再寫文件中使用。
注:第二個(gè)參數(shù)標(biāo)示定位標(biāo)記:
(1) ios_base::beg 文件的開始
(2) ios_base::cur 文件的當(dāng)前位置
(3) ios_base::end 文件的結(jié)尾
9. tellg()或tellp()返回當(dāng)前位置,g和p意義同seekg和seekp。返回值是ios_base::pos_type。
10. clear()函數(shù):清除狀態(tài)。
20.7 條件狀態(tài)
一. 條件狀態(tài)
1 如果一個(gè)流遇到文件結(jié)束符則eof()返回true。
2 如果試圖做一個(gè)無效的操作比如seeking 重定位操作超出了文件尾則bad()返回true,一般地這表示該流由于某種
未定義的方式而被破壞了。
3 如果操作不成功比如打開一個(gè)文件流對(duì)象失敗或遇到一種無效的輸入格式則fail()
返回true 例如
ifstream iFile( filename, ios_base::in );
if ( iFile.fail() ) // 不能打開
error_message( ... );
4 如果其他條件都不為true 則good()返回true
二. 改變狀態(tài)
1. clear()函數(shù),狀態(tài)變?yōu)轱@示。
2. setstate()函數(shù),添加狀態(tài)。參數(shù)設(shè)置為:
ios_base::badbit
ios_base::eofbit
ios_base::failbit
ios_base::goodbit
3. rdstate()獲取成員狀態(tài),返回值ios_base::iostate。
20.8 string 流
1. 包括頭文件#include <sstream>
2. str()返回與ostringstream 類對(duì)象相關(guān)聯(lián)的string 對(duì)象。
20.9 格式狀態(tài)
操 作 符 含 義
boolalpha 把true 和false 表示為字符串
*noboolalpha 把true 和false 表示為0 1
showbase 產(chǎn)生前綴指示數(shù)值的進(jìn)制基數(shù)
*noshowbase 不產(chǎn)生進(jìn)制基數(shù)前綴
showpoint 總是顯示小數(shù)點(diǎn)
*noshowpoint 只有當(dāng)小數(shù)部分存在時(shí)才顯示小數(shù)點(diǎn)
Showpos 在非負(fù)數(shù)值中顯示+
*noshowpos 在非負(fù)數(shù)值中不顯示+
*skipws 輸入操作符跳過空白字符
noskipws 輸入操作符不跳過空白字符
uppercase 在十六進(jìn)制下顯示0X 科學(xué)計(jì)數(shù)法中顯示E
*nouppercase 在十六進(jìn)制下顯示0x 科學(xué)計(jì)數(shù)法中顯示e
*dec 以十進(jìn)制顯示
hex 以十六進(jìn)制顯示
oct 以八進(jìn)制顯示
left 將填充字符加到數(shù)值的右邊
right 將填充字符加到數(shù)值的左邊
Internal 將填充字符加到符號(hào)和數(shù)值的中間
*fixed 以小數(shù)形式顯示浮點(diǎn)數(shù)
scientific 以科學(xué)計(jì)數(shù)法形式顯示浮點(diǎn)數(shù)
flush 刷新ostream 緩沖區(qū)
ends 插入空字符然后刷新ostream 緩沖區(qū)
endl 插入換行符然后刷新ostream 緩沖區(qū)
ws 吃掉 空白字符
// 以下這些要求 #include <iomanip>
setfill(ch) 用ch 填充空白字符
setprecision(n) 將浮點(diǎn)精度設(shè)置為n
setw(w) 按照w 個(gè)字符來讀或者寫數(shù)值
setbase(b) 以進(jìn)制基數(shù)b 輸出整數(shù)值
注*表示缺省的流狀態(tài)
20.10 強(qiáng)類型庫
iostream庫是強(qiáng)類型的例如試圖從一個(gè)ostream 讀數(shù)據(jù)或者寫數(shù)據(jù)到一個(gè)istream都會(huì)在編譯時(shí)刻被捕獲到并標(biāo)記為類型違例。
3.1 什么是內(nèi)核對(duì)象
內(nèi)核對(duì)象就是內(nèi)核中的一塊內(nèi)存,是一個(gè)結(jié)構(gòu),并且只能由內(nèi)核對(duì)象訪問,應(yīng)用程序只能通過調(diào)用Windows提供的函數(shù)來操作內(nèi)核對(duì)象。每個(gè)內(nèi)核對(duì)象都有相同的部分比如安全屬性和使用計(jì)數(shù)器。
3.1.1 內(nèi)核對(duì)象的使用計(jì)數(shù)
內(nèi)核對(duì)象中的使用計(jì)數(shù)和進(jìn)程無關(guān),當(dāng)進(jìn)程第一次創(chuàng)建某個(gè)內(nèi)核對(duì)象時(shí)候使用計(jì)數(shù)變?yōu)?,當(dāng)另一個(gè)進(jìn)程也調(diào)用此內(nèi)核對(duì)象時(shí)計(jì)數(shù)變?yōu)?。當(dāng)進(jìn)程釋放時(shí)或者關(guān)閉內(nèi)核對(duì)象時(shí)(CloseHandle),內(nèi)核的使用計(jì)數(shù)減去1,如果使用計(jì)數(shù)不為0的話,內(nèi)核不會(huì)釋放此內(nèi)核對(duì)象。
3.2.2 安全性
內(nèi)核對(duì)象能夠得到安全描述符的保護(hù),安全描述符定義了誰能夠創(chuàng)建,訪問和使用該對(duì)象,一般在服務(wù)器代碼中使用,客戶端可以忽略。
所有創(chuàng)建內(nèi)核對(duì)象的函數(shù)的參數(shù)都有一個(gè)指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針。
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
只有lpSecurityDescriptor成員和安全屬性有關(guān)。一般此參數(shù)傳遞NULL,表示默認(rèn)的安全描述。
如果需要:
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
HANDLE h = CreateMutex(&sa, FALSE, "XI");
其余進(jìn)程可用用OpenMutex函數(shù)打開,如果權(quán)限可以就返回句柄,如果失敗返回NULL,GetLastError被設(shè)置為ERROR_ACCESS_DENIED。
Windows除了內(nèi)核對(duì)象之外還有GDI和用戶對(duì)象,區(qū)分它們的簡(jiǎn)單辦法就是,創(chuàng)建函數(shù)中帶有安全描述符參數(shù)的就是內(nèi)核對(duì)象。
3.2 進(jìn)程的內(nèi)核對(duì)象句柄表
索引 內(nèi)核對(duì)象內(nèi)存塊得指針 訪問屏蔽(標(biāo)志位的DWORD) 標(biāo)志(標(biāo)志位的DWORD)
1 0x???????? 0x???????? 0x????????
2 0x???????? 0x???????? 0x????????
… … … …
3.2.1 創(chuàng)建內(nèi)核對(duì)象
調(diào)用Create&函數(shù)族來創(chuàng)建相應(yīng)的內(nèi)核對(duì)象,返回的是內(nèi)核對(duì)象的句柄(也有個(gè)說法就是句柄表的索引),如果創(chuàng)建失敗一般會(huì)返回0(NULL),也有的會(huì)返回INVALID_HANDLE_VALUE=-1,比如CreateFile失敗后會(huì)返回后者,失敗的原因有可能是內(nèi)存不足或者是安全問題等等。其他對(duì)內(nèi)核操作的函數(shù)都需要此句柄值作為參數(shù)傳遞進(jìn)去,如果傳遞一個(gè)無效的句柄進(jìn)去,那么GetLastError函數(shù)的值將被置為6(ERROR_INVALID_HANDLE)。
3.2.2 關(guān)閉內(nèi)核對(duì)象
BOOL CloseHandle(HANDLE hobj);
調(diào)用此函數(shù),系統(tǒng)會(huì)了清理進(jìn)程的句柄表中的對(duì)應(yīng)項(xiàng)目,如果使用計(jì)數(shù)器為0,內(nèi)核釋放該內(nèi)核對(duì)象的資源,如果使用計(jì)數(shù)器不為0,說明其他進(jìn)程還在使用此內(nèi)核對(duì)象,則不釋放資源。
當(dāng)進(jìn)程忘記調(diào)用CloseHandle函數(shù),可能造成內(nèi)存泄露,但是當(dāng)進(jìn)程結(jié)束的時(shí)候資源一樣會(huì)被釋放。
如果傳遞的參數(shù)無效,則函數(shù)返回FALSE,并且GetLastError函數(shù)的值被設(shè)置成ERROR_INVALID_HANDLE。如果是DEBUG階段,則返回錯(cuò)誤信息。
3.3 跨越進(jìn)程邊界共享內(nèi)核對(duì)象
3.3.1 對(duì)象句柄的繼承性
- 在父進(jìn)程創(chuàng)建子進(jìn)程的時(shí)候,將參數(shù)SECURITY_ATTRIBUTES結(jié)構(gòu)的Inherithandle字段設(shè)置為TRUE的話,再父進(jìn)程句柄表中標(biāo)示該內(nèi)核對(duì)象的項(xiàng)的標(biāo)志位的值將會(huì)變成TRUE,標(biāo)示該內(nèi)核對(duì)象是可以讓子進(jìn)程繼承的,具有可繼承性(僅僅標(biāo)示 該句柄值具有可繼承性,而內(nèi)核對(duì)象沒有可繼承性)。
- 將CreateProcess的參數(shù)bInherithandle參數(shù)的值設(shè)置為TRUE,標(biāo)示創(chuàng)建的進(jìn)程可以繼承有繼承性的父進(jìn)程句柄。
- 子進(jìn)程創(chuàng)建后不會(huì)先加載程序,它先搜索父進(jìn)程的句柄表將有繼承性的項(xiàng)目原封不動(dòng)的拷貝給自己(索引也沒有變,所以句柄值也不變)。
- 父進(jìn)程可以有3種方式將句柄值傳遞給子進(jìn)程,參數(shù)傳遞,進(jìn)程間通信和環(huán)境變量(GetEnvironmentVariavle函數(shù)解析)。
BOOL
WINAPI
CreateProcess(
__in_opt LPCSTR lpApplicationName,
__inout_opt LPSTR lpCommandLine,
__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in BOOL bInheritHandles,
__in DWORD dwCreationFlags,
__in_opt LPVOID lpEnvironment,
__in_opt LPCSTR lpCurrentDirectory,
__in LPSTARTUPINFOA lpStartupInfo,
__out LPPROCESS_INFORMATION lpProcessInformation
);
注意:
子進(jìn)程再創(chuàng)建其子進(jìn)程,如果滿足上面方式,可以繼續(xù)繼承。
如果父進(jìn)程再創(chuàng)建子進(jìn)程后,再創(chuàng)建句柄,子進(jìn)程不會(huì)被繼承。
3.3.2 改變句柄標(biāo)志
BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
改變句柄的標(biāo)志,目前可改變的標(biāo)志有兩種
#define HANDLE_FLAG_INHERIT 0x00000001 // 繼承標(biāo)志
#define HANDLE_FLAG_PROJECT_FROM_CLOSE 0x00000001 // 保護(hù)不允許關(guān)閉句柄標(biāo)志
可以用OR操作同時(shí)設(shè)置2個(gè)標(biāo)志。第一個(gè)參數(shù)是要設(shè)置的句柄值,第二個(gè)就是要改變的標(biāo)志,第三個(gè)參數(shù)是將標(biāo)志改編成什么值。
BOOL GetHandleInformation(HANDLE hObkect, PDWORD pdwFlags);
獲取當(dāng)前句柄的標(biāo)志的值。
// 設(shè)置句柄值可繼承:
SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
// 設(shè)置句柄不可繼承:
SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, 0);
// 設(shè)置句柄值不可關(guān)閉,受保護(hù):
SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, HANDLE_FLAG_PROJECT_FROM_CLOSE);
// 設(shè)置句柄值可關(guān)閉,不受保護(hù):
SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, 0);
3.3.3 命名對(duì)象
創(chuàng)建內(nèi)核對(duì)象函數(shù)族Create&中的最后一個(gè)參數(shù)是pszName,該參數(shù)是如果傳遞NULL,表示是匿名內(nèi)核對(duì)象,可以通過其他倆種方式來使用其他進(jìn)程的內(nèi)核對(duì)象。當(dāng)pszName參數(shù)傳遞以’\0’(最多長(zhǎng)度為MAX_PATH 260字符)結(jié)尾的字符串時(shí),表示啟用命名對(duì)象,比如進(jìn)程A調(diào)用CreateMutex(NULL, FALSE, “XI”)的時(shí)候,他將創(chuàng)建內(nèi)核對(duì)象名字為“XI”,之后某一時(shí)刻如果進(jìn)程B也調(diào)用CreateMutex(NULL, FALSE, “XI”)函數(shù)他將經(jīng)過以下幾步:
- 判斷內(nèi)核對(duì)象名稱是否相同。
- 判斷內(nèi)核對(duì)象類型是否相同,如果名字相同但是類型不相同則Create&函數(shù)族返回NULL,GetLastError函數(shù)值為6(ERROR_INVALID_HANDLE)。
- 判斷安全性,返回同2步,GetLastError值同2步。
- 如果驗(yàn)證通過則返回句柄(返回句柄的值和該內(nèi)核對(duì)象其他句柄的值不一定相同),GetLastError的值等于ERROR_ALREADY_EXISTS。
也可以用Open&函數(shù)族來打開已經(jīng)創(chuàng)建的句柄,成功后GetLastError也不會(huì)被設(shè)置。具體如下
HANDLE Open&(DWORD, BOOL, PCSTR);
第一個(gè)參數(shù):表示訪問權(quán)限。
第二個(gè)參數(shù):表示新創(chuàng)建的句柄是否有繼承性(注意不是內(nèi)核對(duì)象!)。
第三個(gè)參數(shù):不能傳遞NULL。如果該句柄不存在則返回NULL,GetLastError被設(shè)置為2(ERROR_FILE_NOT_FOUND)。
3.3.4 終端服務(wù)器的名字空間
Globad,Local,Session程序保留關(guān)鍵字,具體的沒弄明白,理解的就是說當(dāng)服務(wù)器的時(shí)候,客戶端可以訪問以這些名字開頭的內(nèi)核對(duì)象。
3.3.5 復(fù)制對(duì)象句柄
BOOL DuplicateHandle(
HANDLE hSourceProcessHandle,
HANDLE hSourceHandle,
HANDLE TargetProcessHandle,
PHANDLE phTargetHandle,
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwOptions);
執(zhí)行DuplicateHandle函數(shù)的進(jìn)程為ProcessC,原進(jìn)程為ProcessS,目標(biāo)進(jìn)程為ProcessT。則hSourceProcessHandle為進(jìn)程ProcessS的進(jìn)程句柄,TargetProcessHandle為進(jìn)程ProcessT的進(jìn)程句柄,ProcessC將句柄hSourceHandle從ProcessC拷貝到ProcessT中,值存在phTargetHandle中,dwDesiredAccess新句柄的反問權(quán)限,bInheritHandle新句柄的繼承性,參數(shù)dwOptions有兩種類型分別是:
DUPLICATE_SAME_ACCESS忽略參數(shù)dwDesiredAccess,新句柄和原進(jìn)程句柄具有相同的反問權(quán)限。
DUPLICATE_CLOSE_SOURCE關(guān)閉ProcessS中的拷貝句柄,內(nèi)核對(duì)象的計(jì)數(shù)不變。
HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);
HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT);
HANDLE hObjProcessT;
DuplicateHandle(GetCurrentProcess(), hObjProcessS, hProcessT , &hObjProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(hObjProcessS);
CloseHandle(hProcessT);
注意:
一般DuplicateHandle函數(shù)沒有在三個(gè)進(jìn)程中使用,因?yàn)楹茈y知道原進(jìn)程的句柄值。
要使用IPC機(jī)制通知目標(biāo)進(jìn)程,新句柄已經(jīng)拷貝過去。