函數(shù)原型 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函數(shù)
CreateProcess函數(shù)用于創(chuàng)建進(jìn)程:
- BOOL CreateProcess(
- PCTSTR pszApplicationName,
- PTSTR pszCommandLine,
- PSECURITY_ATTRIBUTES psaProcess,
- PSECURITY_ATTRIBUTES psaThread,
- BOOL bInheritHandles,
- DWORD fdwCreate,
- PVOID pvEnvironment,
- PCTSTR pszCurDir,
- PSTARTUPINFO psiStartInfo,
- PPROCESS_INFORMATION ppiProcInfo);
線程調(diào)用CreateProcess時,系統(tǒng)會創(chuàng)建一個進(jìn)程內(nèi)核對象,將其引用計數(shù)初始化為1(進(jìn)程內(nèi)核對象并不是進(jìn)程本身,它只是操作系統(tǒng)用來管理進(jìn)程的數(shù)據(jù)結(jié)構(gòu),其中包含了進(jìn)程的一些統(tǒng)計信息)。然后系統(tǒng)為新進(jìn)程開辟虛擬地址空間,并將可執(zhí)行文件的代碼和數(shù)據(jù)以及所需的DLL裝載到該地址空間中。接著系統(tǒng)為進(jìn)程主線程創(chuàng)建線程內(nèi)核對象,并將其引用計數(shù)初始為1(同進(jìn)程一樣,線程內(nèi)核對象也不是線程本身,而且操作系統(tǒng)用來管理線程的數(shù)據(jù)結(jié)構(gòu))。主線程將鏈接器設(shè)置的入口點函數(shù)作為C/C++運行時啟動函數(shù)調(diào)用,這些啟動函數(shù)最終又調(diào)用代碼中的入口點函數(shù)如WinMain、wWinMain、main和 wmain。當(dāng)操作系統(tǒng)成功創(chuàng)建了新的進(jìn)程和主線程后,CreateProcess返回TRUE。以上是CreateProcess的簡要介紹,下面我們來詳細(xì)討論它的參數(shù)。
pszApplicationName和pszCommandLine
pszApplicationName和pszCommandLine分別表示進(jìn)程使用的可執(zhí)行文件名和向其傳遞的命令行字符串,我們先來看看 pszCommandLine參數(shù)。注意pszCommandLine是PTSTR,這意味著你必須為其傳遞指向非常量字符串的地址。 CreateProcess內(nèi)部會更改向其傳遞的命令行字符串,但在CreateProcess返回之前,它會將該字符串恢復(fù)原樣。這一點是非常重要的,因為如果你向CreateProcess傳遞的命令行字符串位于進(jìn)程的只讀存儲區(qū),就會發(fā)生Access Violation錯誤。比如,下面的代碼執(zhí)行時會觸發(fā)Access Violation,因為微軟的C/C++編譯器會把常量字符串放入只讀存儲區(qū)(注意早期的微軟C/C++編譯器會將常量字符串放在可讀寫存儲區(qū),因此下面的代碼在舊的編譯環(huán)境下不會出錯):
- STARTUPINFO si = { sizeof(si) };
- PROCESS_INFORMATION pi;
- CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &pi);
解決這個問題的方法很簡單,將命令行字符串復(fù)制到臨時緩沖區(qū)既可,如下所示:
- STARTUPINFO si = { sizeof(si) };
- PROCESS_INFORMATION pi;
- TCHAR szCommandLine[] = TEXT("NOTEPAD");
- CreateProcess(NULL, szCommandLine, NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &pi);
微軟在其C++編譯器選項中提供了/GF開關(guān),/GF打開時,程序中所有用到的常量字符串將只維護(hù)單一副本,且位于只讀存儲部分。在調(diào)用 CreateProcess時,開發(fā)人員應(yīng)該打開/GF開關(guān)并使用緩沖區(qū)。我們希望微軟在未來版本的Windows中會改進(jìn)CreateProcess,使其接受常量字符串作為命令行參數(shù),并在其內(nèi)部分配/釋放臨時緩沖區(qū)而不是讓API調(diào)用者來做。另外,假如你使用常量ANSI字符串作為 CreateProcess參數(shù),并不會發(fā)生Access Violation錯誤,我們在前面的章節(jié)已經(jīng)提到過,許多WinAPI函數(shù)的ANSI版本會將ANSI參數(shù)轉(zhuǎn)換為UNIDOE編碼后調(diào)用其 Unicode版本,CreateProcess會把ANSI字符串轉(zhuǎn)換為Unicode編碼后放在臨時緩沖區(qū),并調(diào)用Unicode版的 CreateProcess,因此不會觸發(fā)Access Violation。
pszCommandLine參數(shù)指定了 CreateProcess創(chuàng)建新進(jìn)程所需的完整命令行。當(dāng)CreateProcess解析該參數(shù)時,它會檢查命令行參數(shù)中的第一個標(biāo)記,并將其作為進(jìn)程要執(zhí)行的可執(zhí)行文件名,如果該文件名沒有指定后綴,函數(shù)將把它當(dāng)作exe文件。CreateProcess會按下面的順序查找該文件:
1. 包含當(dāng)前進(jìn)程可執(zhí)行文件的目錄
2. 當(dāng)前進(jìn)程的當(dāng)前目錄
3. Windows系統(tǒng)目錄,既GetSystemDirectory返回的目錄
4. Windows目錄
5. PATH環(huán)境變量列出的目錄
當(dāng)然,如果文件名包含了完整路徑,系統(tǒng)將會在該路徑中查找文件而不會再做上面的搜索。如果系統(tǒng)找到了可執(zhí)行文件,它會創(chuàng)建一個新的進(jìn)程并把可執(zhí)行文件的代碼和數(shù)據(jù)映射到進(jìn)程的地址空間,然后調(diào)用CRT啟動函數(shù)(linker選項卡中的入口點函數(shù)),接著CRT啟動函數(shù)檢查命令行參數(shù),過濾掉其中的可執(zhí)行文件部分,并把剩下字符串的地址作為pszCmdLine傳給wWinMain/WinMain。
以上情形都是在pszApplicationName為NULL時發(fā)生的。 pszApplicationName指定了進(jìn)程要執(zhí)行的可執(zhí)行文件的名稱,假如沒有指定文件后綴,系統(tǒng)并不會做任何處理。 pszApplicationName不包含完整路徑時,CreateProcess只從當(dāng)前目錄中查找可執(zhí)行文件,查找失敗時函數(shù)失敗并返回 FALSE。即使指定了pszApplicationName,CreateProcess仍然會將pszCommandLine參數(shù)作為新進(jìn)程的命令行。比如下面的代碼:
- // Make sure that the path is in a read/write section of memory.
- TCHAR szPath[] = TEXT("WORDPAD README.TXT");
- // Spawn the new process.
- CreateProcess(TEXT("C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE"),szPath,...);
執(zhí)行上面代碼時,系統(tǒng)會打開notepad.exe(記事本),但它的命令行卻是WORDPAD README.TXT(WORDPAD是寫字板),這看上去非常奇怪,但CreateProcess就是這樣工作的。這種 pszApplicationName提供的特性被用來支持Windows的POSIX子系統(tǒng)。
psaProcess, psaThread和bInheritHandles
創(chuàng)建新進(jìn)程時,系統(tǒng)會創(chuàng)建一個進(jìn)程內(nèi)核對象和一個線程內(nèi)核對象(用于進(jìn)程的主線程),和其它內(nèi)核對象一樣,創(chuàng)建者(在這兒是父進(jìn)程)必須指定其安全屬性。psaProcess和psaThread分別指定了新進(jìn)程的進(jìn)程內(nèi)核對象和線程內(nèi)核對象的安全屬性。將其設(shè)為NULL時,系統(tǒng)為對應(yīng)的內(nèi)核對象指定默認(rèn)的安全屬性。你可以創(chuàng)建SECURITY_ATTRIBUTES類型的變量,設(shè)置其中各個域的值然后將變量地址傳遞給psaProcess或 psaThread,以應(yīng)用指定的安全屬性。正如在第3章討論內(nèi)核對象時談到的,當(dāng)你想要控制新的進(jìn)內(nèi)核對象的句柄能否被父進(jìn)程以后創(chuàng)建的子進(jìn)程繼承時,你應(yīng)該設(shè)置SECURITY_ATTRIBUTES變量的bInheritHandle域的值。
下面的Inherit.cpp展示了內(nèi)核對象句柄繼承。假設(shè)執(zhí)行該代碼的進(jìn)程為A,它調(diào)用CreateProcess創(chuàng)建了進(jìn)程B,接著又創(chuàng)建了子進(jìn)程C。注意調(diào)用CreateProcess時A使用的psaProcess、psaThread和bInheritHandles參數(shù),代碼注釋很詳細(xì)的描述了這些參數(shù)的作用:
- /************************************************************
- Module name: Inherit.cpp
- Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
- ************************************************************/
- #include <Windows.h>
- int WINAPI _tWinMain (HINSTANCE hInstanceExe, HINSTANCE,
- PTSTR pszCmdLine, int nCmdShow) {
- // Prepare a STARTUPINFO structure for spawning processes.
- STARTUPINFO si = { sizeof(si) };
- SECURITY_ATTRIBUTES saProcess, saThread;
- PROCESS_INFORMATION piProcessB, piProcessC;
- TCHAR szPath[MAX_PATH];
- // Prepare to spawn Process B from Process A.
- // The handle identifying the new process
- // object should be inheritable.
- saProcess.nLength = sizeof(saProcess);
- saProcess.lpSecurityDescriptor = NULL;
- saProcess.bInheritHandle = TRUE;
- // The handle identifying the new thread
- // object should NOT be inheritable.
- saThread.nLength = sizeof(saThread);
- saThread.lpSecurityDescriptor = NULL;
- saThread.bInheritHandle = FALSE;
- // Spawn Process B.
- _tcscpy_s(szPath, _countof(szPath), TEXT("ProcessB"));
- CreateProcess(NULL, szPath, &saProcess, &saThread,
- FALSE, 0, NULL, NULL, &si, &piProcessB);
- // The pi structure contains two handles
- // relative to Process A:
- // hProcess, which identifies Process B's process
- // object and is inheritable; and hThread, which identifies
- // Process B's primary thread object and is NOT inheritable.
- // Prepare to spawn Process C from Process A.
- // Since NULL is passed for the psaProcess and psaThread
- // parameters, the handles to Process C's process and
- // primary thread objects default to "noninheritable."
- // If Process A were to spawn another process, this new
- // process would NOT inherit handles to Process C's process
- // and thread objects.
- // Because TRUE is passed for the bInheritHandles parameter,
- // Process C will inherit the handle that identifies Process
- // B's process object but will not inherit a handle to
- // Process B's primary thread object.
- _tcscpy_s(szPath, _countof(szPath), TEXT("ProcessC"));
- CreateProcess(NULL, szPath, NULL, NULL,
- TRUE, 0, NULL, NULL, &si, &piProcessC);
- return(0);
- }
fdwCreate
fdwCreate參數(shù)用來控制進(jìn)程被創(chuàng)建時的行為,下面列出了它可能的取值:
·DEBUG_PROCESS:父進(jìn)程將調(diào)試子進(jìn)程及子進(jìn)程創(chuàng)建的所有進(jìn)程,指定該參數(shù)后,在子進(jìn)程或子進(jìn)程創(chuàng)建的任意進(jìn)程中發(fā)生特定事件時系統(tǒng)將通知父進(jìn)程
·DEBUG_ONLY_THIS_PROCESS:父進(jìn)程將調(diào)試子進(jìn)程,指定該參數(shù)后,在子進(jìn)程中發(fā)生特定事件時系統(tǒng)將通知父進(jìn)程
·CREATE_SUSPENDED:進(jìn)程創(chuàng)建后其主線程暫不執(zhí)行。此時父進(jìn)程可以在子進(jìn)程運行之前更改子進(jìn)程地址空間中的數(shù)據(jù)、更改子進(jìn)程主線程優(yōu)先級、將子進(jìn)程添加到作業(yè)中等。父進(jìn)程完成其更改后,可以調(diào)用ResumeThread函數(shù)恢復(fù)子進(jìn)程主線程運行
·DETACHED_PROCESS:系統(tǒng)將阻止CUI程序向其父進(jìn)程的CUI窗口寫入其輸出。當(dāng)父進(jìn)程為CUI進(jìn)程時,創(chuàng)建的CUI子進(jìn)程默認(rèn)使用父進(jìn)程的CUI窗口(如cmd.exe程序)。指定該參數(shù)后,新進(jìn)程在需要輸出到窗口時必須調(diào)用AllocConsole創(chuàng)建CUI窗口
·CREATE_NEW_CONSOLE:系統(tǒng)自動為新進(jìn)程創(chuàng)建一個CUI窗口,該標(biāo)志不能與DETACHED_PROCESS同時使用
·CREATE_NO_WINDOW:系統(tǒng)不為新進(jìn)程創(chuàng)建CUI窗口,使用該標(biāo)志可以創(chuàng)建不含窗口的CUI程序
·CREATE_NEW_PROCESS_GROUP:新進(jìn)程將作為一個新的進(jìn)程組的根進(jìn)程,新的進(jìn)程組將包含以根進(jìn)程為祖先的所有進(jìn)程。用戶在進(jìn)程組中的某個進(jìn)程CUI窗口中按下Ctrl+C或Ctrl+B時,系統(tǒng)將通知進(jìn)程組中的所有進(jìn)程這一事件
·CREATE_DEFAULT_ERROR_MODE:子進(jìn)程不繼承父進(jìn)程的任何錯誤標(biāo)志
·CREATE_SEPARATE_WOW_VDM:僅用于16位Windows程序,不譯
·CREATE_SHARED_WOW_VDM:僅用于16位Windows程序,不譯
·CREATE_UNICODE_ENVIRONMENT:子進(jìn)程的環(huán)境塊為Unicode字符串。進(jìn)程的環(huán)境塊默認(rèn)只包含ANSI字符串
·CREATE_FORCEDOS:強(qiáng)制系統(tǒng)運行內(nèi)嵌在16位OS/2系統(tǒng)中的MS-DOS程序
·CREATE_BREAKAWAY_FROM_JOB:當(dāng)父進(jìn)程屬于某個作業(yè)時,新建的子進(jìn)程將不再與該作業(yè)關(guān)聯(lián)
·EXTENDED_STARTUPINFO_PRESENT:傳遞給CreateProcess函數(shù)的psiStartInfo參數(shù)是STARTUPINFOEX類型的變量
fdwCreate參數(shù)也可以用于設(shè)置新進(jìn)程的優(yōu)先級。但你不必這樣做,對大多數(shù)應(yīng)用你也不應(yīng)該這樣做——系統(tǒng)會為新進(jìn)程分配默認(rèn)優(yōu)先級。表4-5列出了可能的優(yōu)先級常量:
這些常量決定了進(jìn)程中的線程在CPU中調(diào)度的優(yōu)先級,我們在188頁的“優(yōu)先級概述”中會討論該問題。
pvEnvironment
參數(shù)pvEnvironment指向一塊內(nèi)存區(qū)域,其中包含新進(jìn)程用到的環(huán)境字符串。大多數(shù)情況下,你可以為其傳遞NULL,此時新進(jìn)程將繼承父進(jìn)程的環(huán)境字符串。
pszCurDir
參數(shù)pszCurDir允許父進(jìn)程設(shè)置子進(jìn)程的當(dāng)前驅(qū)動器和目錄。如果該參數(shù)為NULL,子進(jìn)程將使用父進(jìn)程的當(dāng)前驅(qū)動器和目錄作為其當(dāng)前驅(qū)動器和目錄。如果pszCurDir非空,則其必須指向一個包含驅(qū)動器標(biāo)識的以0結(jié)尾的路徑字符串。
psiStartInfo
psiStartInfo是指向STARTUPINFO或STARTUPINFOEX變量的提針:
- typedef struct _STARTUPINFO {
- DWORD cb;
- PSTR lpReserved;
- PSTR lpDesktop;
- PSTR lpTitle;
- DWORD dwX;
- DWORD dwY;
- DWORD dwXSize;
- DWORD dwYSize;
- DWORD dwXCountChars;
- DWORD dwYCountChars;
- DWORD dwFillAttribute;
- DWORD dwFlags;
- WORD wShowWindow;
- WORD cbReserved2;
- PBYTE lpReserved2;
- HANDLE hStdInput;
- HANDLE hStdOutput;
- HANDLE hStdError;
- } STARTUPINFO, *LPSTARTUPINFO;
- typedef struct _STARTUPINFOEX {
- STARTUPINFO StartupInfo;
- struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;
- } STARTUPINFOEX, *LPSTARTUPINFOEX;
Windows創(chuàng)建新進(jìn)程時會使用STARTUPINFO(EX)的成員變量,大多數(shù)情況下可以使用這些變量的默認(rèn)值,此時你應(yīng)該該將其cb域設(shè)置為結(jié)構(gòu)的大小,并將其余域清0,如下:
- STARTUPINFO si = { sizeof(si) };
- CreateProcess(..., &si, ...);
許多開發(fā)人員常常會忘記執(zhí)行上述操作,如果你沒有清空其內(nèi)容,STARTUPINFO(EX)的內(nèi)容會是調(diào)用線程堆棧上的一些數(shù)據(jù)。將這些垃圾數(shù)據(jù)傳遞給CreateProcess可能導(dǎo)致無法預(yù)料的結(jié)果,為了讓CreateProcess正常工作,你必須將STARTUPINFO(EX)中沒有用到的域清0。
表4-6列出了STARTUPINFO(EX)結(jié)構(gòu)的成員,注意有些成員只在GUI應(yīng)用中生效,而有些則只在CUI應(yīng)用中生效:
現(xiàn)在我們來討論dwFlags成員。dwFlags包含一組標(biāo)志用來指示如何創(chuàng)建子進(jìn)程,其中大多數(shù)標(biāo)志只是告訴CreateProcess是否使用STARTUPINFO結(jié)構(gòu)中的某個成員,表4-7列出了dwFlags的可取值:
另外兩個標(biāo)志 STARTF_FORCEONFEEDBACK和STARTF_FORCEOFFFEEDBACK可以控制在創(chuàng)建子進(jìn)程時如何顯示鼠標(biāo)指針。由于 Windows支持搶先式多任務(wù)調(diào)度,因此你可以在創(chuàng)建子程并等待子進(jìn)程初始化時,執(zhí)行另外的程序。如果你指定了 STARTF_FORCEONFEEDBACK,Windows會在新進(jìn)程初始化時將鼠標(biāo)光標(biāo)指針更改為“后臺運行”,如下圖:
這個標(biāo)志意味著系統(tǒng)后臺正在處理某些任務(wù)(在這里是創(chuàng)建并初始化子進(jìn)程),但你依然可以繼續(xù)使用系統(tǒng)。當(dāng)你指定了STARTF_FORCEOFFFEEDBACK標(biāo)志時,CreateProcess不會更改鼠標(biāo)指針樣式。
如果指定了STARTF_FORCEONFEEDBACK,且子進(jìn)程在CreateProcess調(diào)用后2秒內(nèi)執(zhí)行了GUI調(diào)用,CreateProcess會等待子進(jìn)程顯示窗口。如果該GUI調(diào)用后5秒之內(nèi)還沒有窗口顯示,CreateProcess會將鼠標(biāo)指針恢復(fù)原狀,否則繼續(xù)等待5秒,如果在這5秒之內(nèi)子進(jìn)程調(diào)用了GetMessage函數(shù),CreateProcess會認(rèn)為子進(jìn)程已經(jīng)完成初始化并將鼠標(biāo)指針復(fù)位。
STARTUPINFO的wShowWindow變量將傳遞給wWinMain/WinMain的最后一個參數(shù)nCmdShow,它的取值是 ShowWindow函數(shù)接受的參數(shù)值之一,通常被指定為SW_SHOWNORMAL、SW_SHOWMINNOACTIVE或 SW_SHOWDEFAULT。
在結(jié)束本節(jié)之前,我們來看看STARTUPINFOEX結(jié)構(gòu)。通過使用同時兼容STARTUPINFOEX和STARTUPINFO結(jié)構(gòu)的參數(shù)psiStartInfo,微軟在保持CreateProcess簽名的同時提高了其擴(kuò)展性。下面是STARTUPINFOEX結(jié)構(gòu)的定義:
- typedef struct _STARTUPINFOEXA {
- STARTUPINFOA StartupInfo;
- struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;
- } STARTUPINFOEXA, *LPSTARTUPINFOEXA;
- typedef struct _STARTUPINFOEXW {
- STARTUPINFOW StartupInfo;
- struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList;
- } STARTUPINFOEXW, *LPSTARTUPINFOEXW;
lpAttributeList(屬性鏈表)是_PROC_THREAD_ATTRIBUTE_LIST結(jié)構(gòu)的鏈表,其中每個結(jié)構(gòu)包含一個key/value對,目前,_PROC_THREAD_ATTRIBUTE_LIST中key的取值只能是下面兩種:
- PROC_THREAD_ATTRIBUTE_HANDLE_LIST:告訴 CreateProcess指定的句柄可被子進(jìn)程繼承,當(dāng)然該句柄必須是可繼承的(其繼承標(biāo)志位為1),且無需將CreateProcess的 bInheritHandles參數(shù)設(shè)置為TRUE。使用該標(biāo)志可以指定子進(jìn)程繼承可繼承句柄的子集而不是全部。這對于需要在不同的安全環(huán)境中創(chuàng)建子進(jìn)程的進(jìn)程而言非常重要,在這種情況下,由于安全原因,某些子進(jìn)程可能不應(yīng)該繼承全部的可繼承句柄。
- PROC_THREAD_ATTRIBUTE_PARENT_PROCESS:指定一個進(jìn)程句柄,指定的進(jìn)程(包括其可繼承句柄、親緣性、優(yōu)先級等等)會替代調(diào)用CreateProcess的當(dāng)前進(jìn)程,成為子進(jìn)程的父進(jìn)程。如果當(dāng)前進(jìn)程在調(diào)用CreateProcess時指定了DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS,重新指定父進(jìn)程并不影響原父進(jìn)程調(diào)試過程,在子進(jìn)程中發(fā)生的特定事件仍然會報告給原父進(jìn)程。
屬性鏈表的內(nèi)容是不透明的,因此我們需要一些函數(shù)來創(chuàng)建空的屬性鏈表。創(chuàng)建屬性鏈表需要以下幾個步驟,首先,為其分配存儲空間,然后向其中添加鍵值對。函數(shù)InitializeProcThreadAttributeList用來創(chuàng)建新的屬性鏈表并為其分配存儲空間:
- BOOL InitializeProcThreadAttributeList(
- PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
- DWORD dwAttributeCount,
- DWORD dwFlags,
- PSIZE_T pSize);
參數(shù)dwFlags必須指定為0,你可以先用如下方式獲得屬性鏈表所需的空間大。
- SIZE_T cbAttributeListSize = 0;
- BOOL bReturn = InitializeProcThreadAttributeList(
- NULL, 1, 0, &cbAttributeListSize);
- // bReturn is FALSE but GetLastError() returns ERROR_INSUFFICIENT_BUFFER
cbAttributeListSize 返回創(chuàng)建屬性鏈表所需的內(nèi)存大小,該大小與dwAttributeCount參數(shù)相關(guān),dwAttributeCount指定了屬性鏈表中的 key/value對的數(shù)目。接下來你可以用cbAttributeListSize為屬性鏈表分配空間:
- pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
- HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
然后再次調(diào)用InitializeProcThreadAttributeList初始化屬性鏈表的內(nèi)容:
- bReturn = InitializeProcThreadAttributeList(
- pAttributeList, 1, 0, &cbAttributeListSize);
當(dāng)屬性鏈表初始化完成后,就可以調(diào)用UpdateProcThreadAttribute向其添加鍵/值對了:
- BOOL UpdateProcThreadAttribute(
- PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
- DWORD dwFlags,
- DWORD_PTR Attribute,
- PVOID pValue,
- SIZE_T cbSize,
- PVOID pPreviousValue,
- PSIZE_T pReturnSize);
pAttributeList是要添加鍵/值對的屬性列表,Attribute可取 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS或PROC_THREAD_ATTRIBUTE_HANDLE_LIST,取前者時,pValue參數(shù)應(yīng)指向另外一個進(jìn)程的句柄,cbSize取值應(yīng)為sizeof(HANDLE),否則,pValue指向子進(jìn)程要繼承的所有內(nèi)核對象的句柄數(shù)組,cbSize取值應(yīng)是sizeof(HANDLE)乘以該數(shù)組的大小。參數(shù)dwFlags、pPreviousValue和 pReturnSize是保留參數(shù),應(yīng)分別賦0、NULL和NULL。
注意,如果在創(chuàng)建子進(jìn)程時為其指定新的父進(jìn)程,既使用了 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,那么在使用 PROC_THREAD_ATTRIBUTE_HANDLE_LIST時,pValue指向的句柄數(shù)組中的句柄應(yīng)該是新父進(jìn)程句柄表中對象的句柄,而不是調(diào)用CreateProcess的進(jìn)程所有。
當(dāng)你在CreateProcess的dwCreateFlags參數(shù)中指定了EXTENDED_STARTUPINFO_PRESENT時,你應(yīng)該向CreateProcess的pStartupInfo參數(shù)傳遞一個STARTUPINFOEX結(jié)構(gòu)的指針,如下面所示:
- STARTUPINFOEX esi = { sizeof(STARTUPINFOEX) };
- esi.lpAttributeList = pAttributeList;
- bReturn = CreateProcess(
- ..., EXTENDED_STARTUPINFO_PRESENT, ...
- &esi.StartupInfo, ...);
其中pAttributeList是按前面的方法創(chuàng)建的屬性列表。當(dāng)你不再需要該屬性列表時,應(yīng)該調(diào)用下面的方法回收為其分配的內(nèi)存:
- VOID DeleteProcThreadAttributeList(
- PPROC_THREAD_ATTRIBUTE_LIST pAttributeList);
最后,應(yīng)用程序可以調(diào)用GetStartupInfo獲得由其父進(jìn)程在CreateProcess中指定的STARTUPINFO結(jié)構(gòu)的拷貝:
- VOID GetStartupInfo(LPSTARTUPINFO pStartupInfo);
注意,無論父進(jìn)程在調(diào)用CreateProcess時參數(shù)pStartupInfo指向STARTUPINFO還是STARTUPINFOEX結(jié)構(gòu),GetStartupInfo總是返回STARTUPINFO結(jié)構(gòu)的拷貝。
ppiProcInfo
ppiProcInfo 參數(shù)是PROCESS_INFORMATION結(jié)構(gòu)的指針,調(diào)用CreateProcess時該結(jié)構(gòu)必須由開發(fā)人員手動分配。CreateProcess 在返回前會填充ppiProcInfo指向的結(jié)構(gòu)的內(nèi)容。PROCESS_INFORMATION定義如下:
- typedef struct _PROCESS_INFORMATION {
- HANDLE hProcess;
- HANDLE hThread;
- DWORD dwProcessId;
- DWORD dwThreadId;
- } PROCESS_INFORMATION;
CreateProcess會創(chuàng)建一個進(jìn)程內(nèi)核對象和一個線程內(nèi)核對象,創(chuàng)建初期,系統(tǒng)將其引用計數(shù)分別置為1。CreateProcess返回之前會獲得這兩個對象的訪問權(quán)限,這樣每個對象的引用計數(shù)會分別增加1,CreateProcess返回之后,兩個對象的引用計數(shù)變成2。這意味著如果系統(tǒng)要釋放CreateProcess進(jìn)程/線程內(nèi)核對象,相應(yīng)的進(jìn)程/線程必須終止,并且調(diào)用CreateProcess的線程必須調(diào)用 CloseHandle關(guān)閉相應(yīng)的對象句柄,這樣才能使得其引用計數(shù)變?yōu)?,系統(tǒng)方能釋放。