CreateProcess
百科名片
WIN32API函數CreateProcess用來建立一個新的進程和它的主線程,這個新進程運行指定的可執行文件。windows
編輯本段
函數原型
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
);
編輯本段參數
1.lpApplicationName:
指向一個NULL結尾的、用來指定可執行模塊的字符串。
這個字符串能夠是可執行模塊的絕對路徑,也能夠是相對路徑,在後一種狀況下,函數使用當前驅動器和目錄創建可執行模塊的路徑。
這個參數能夠被設爲NULL,在這種狀況下,可執行模塊的名字必須處於 lpCommandLine 參數的最前面並由空格符與後面的字符分開。
這個被指定的模塊能夠是一個Win32應用程序。若是適當的子系統在當前計算機上可用的話,它也能夠是其餘類型的模塊(如MS-DOS 或 OS/2)。
在Windows NT中,若是可執行模塊是一個16位的應用程序,那麼這個參數應該被設置爲NULL而且因該在lpCommandLine參數中指定可執行模塊的名稱。16位的應用程序是以DOS虛擬機或Win32上的Windows(WOW) 爲進程的方式運行。
2.lpCommandLine:
指向一個NULL結尾的、用來指定要運行的命令行。
這個參數能夠爲空,那麼函數將使用參數指定的字符串看成要運行的程序的命令行。
若是lpApplicationName和lpCommandLine參數都不爲空,那麼lpApplicationName參數指定將要被運行的模塊,lpCommandLine參數指定將被運行的模塊的命令行。新運行的進程可使用GetCommandLine函數得到整個命令行。C語言程序可使用argc和argv參數。
若是lpApplicationName參數爲空,那麼這個字符串中的第一個被空格分隔的要素指定可執行模塊名。若是文件名不包含擴展名,那麼.exe將被假定爲默認的擴展名。若是文件名以一個點(.)結尾且沒有擴展名,或文件名中包含路徑,.exe將不會被加到後面。若是文件名中不包含路徑,Windows將按照以下順序尋找這個可執行文件:
1).當前應用程序的目錄。
2).父進程的目錄。
3).Windows 95:Windows系統目錄,可使用GetSystemDirectory函數得到。
Windows NT:32位Windows系統目錄。可使用GetSystemDirectory函數得到,目錄名是SYSTEM32。
4).在Windows NT中:16位Windows系統目錄。不可使用Win32函數得到這個目錄,可是它會被搜索,目錄名是SYSTEM。
5).Windows目錄。可使用GetWindowsDirectory函數得到這個目錄。
6).列在PATH環境變量中的目錄。
若是被建立的進程是一個以MS-DOS或16位Windows爲基礎的應用程序,lpCommandLine參數應該是一個以可執行文件的文件名做爲第一個要素的絕對路徑,由於這樣作可使32位Windows程序工做的很好,這樣設置lpCommandLine參數是最強壯的。
注意:Visual C++ 2005之後的版本中,若是向CreateProcess函數傳遞一個常量指針做爲命令行參數的話,將會發生訪問違規錯誤。緣由是系統在[1]執行該函數時會修改lpCommandLine所指向的字符串(好比解釋轉義字符等)。所以,在調用此函數前,應該定義一個臨時字符數組變量來保存命令行參數,並將這個臨時變量做爲lpCommandLine參數傳遞.
3.lpProcessAttributes:
指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄能夠被子進程繼承。若是lpProcessAttributes參數爲空(NULL),那麼句柄不能被繼承。
在Windows NT中:SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員指定了新進程的安全描述符,若是參數爲空,新進程使用默認的安全描述符。
在Windows95中:SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員被忽略。
4.lpThreadAttributes:
指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的指向線程的句柄能夠被子進程繼承。若是lpThreadAttributes參數爲空(NULL),那麼句柄不能被繼承。
在Windows NT中,SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員指定了主線程的安全描述符,若是參數爲空,主線程使用默認的安全描述符。
在Windows95中:SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員被忽略。
5.bInheritHandles:
指示新進程是否從調用進程處繼承了句柄。
若是參數的值爲真,調用進程中的每個可繼承的打開句柄都將被子進程繼承。被繼承的句柄與原進程擁有徹底相同的值和訪問權限。
5.dwCreationFlags:
指定附加的、用來控制優先類和進程的建立的標誌。如下的建立標誌能夠以除下面列出的方式外的任何方式組合後指定。
(1)值:CREATE_DEFAULT_ERROR_MODE
含義:新的進程不繼承調用進程的錯誤模式。CreateProcess函數賦予新進程當前的默認錯誤模式做爲替代。應用程序能夠調用SetErrorMode函數設置當前的默認錯誤模式。
這個標誌對於那些運行在沒有硬件錯誤環境下的多線程外殼程序是十分有用的。
對於CreateProcess函數,默認的行爲是爲新進程繼承調用者的錯誤模式。設置這個標誌以改變默認的處理方式。
(2)值:CREATE_NEW_CONSOLE
含義:新的進程將使用一個新的控制檯,而不是繼承父進程的控制檯。這個標誌不能與DETACHED_PROCESS標誌一塊兒使用。
(3)值:CREATE_NEW_PROCESS_GROUP
含義:新進程將使一個進程樹的根進程。進程樹種的所有進程都是根進程的子進程。新進程樹的用戶標識符與這個進程的標識符是相同的,由lpProcessInformation參數返回。進程樹常用GenerateConsoleCtrlEvent函數容許發送CTRL+C或CTRL+BREAK信號到一組控制檯進程。
(4)值:CREATE_SEPARATE_WOW_VDM
含義:(只適用於Windows NT)這個標誌只有當運行一個16位的Windows應用程序時纔是有效的。若是被設置,新進程將會在一個私有的虛擬DOS機(VDM)中運行。另外,默認狀況下全部的16位Windows應用程序都會在同一個共享的VDM中以線程的方式運行。單獨運行一個16位程序的優勢是一個應用程序的崩潰只會結束這一個VDM的運行;其餘那些在不一樣VDM中運行的程序會繼續正常的運行。一樣的,在不一樣VDM中運行的16位Windows應用程序擁有不一樣的輸入隊列,這意味着若是一個程序暫時失去響應,在獨立的VDM中的應用程序可以繼續得到輸入。
(5)值:CREATE_SHARED_WOW_VDM
含義:(只適用於Windows NT)這個標誌只有當運行一個16位的Windows應用程序時纔是有效的。若是WIN.INI中的Windows段的DefaultSeparateVDM選項被設置爲真,這個標識使得CreateProcess函數越過這個選項並在共享的虛擬DOS機中運行新進程。
(6)值:CREATE_SUSPENDED
含義:新進程的主線程會以暫停的狀態被建立,直到調用ResumeThread函數被調用時才運行。
(7)值:CREATE_UNICODE_ENVIRONMENT
含義:若是被設置,由lpEnvironment參數指定的環境塊使用Unicode字符,若是爲空,環境塊使用ANSI字符。
(8)值:DEBUG_PROCESS
含義:若是這個標誌被設置,調用進程將被看成一個調試程序,而且新進程會被看成被調試的進程。系統把被調試程序發生的全部調試事件通知給調試器。
若是你使用這個標誌建立進程,只有調用進程(調用CreateProcess函數的進程)能夠調用WaitForDebugEvent函數。
(9)值:DEBUG_ONLY_THIS_PROCESS
含義:若是此標誌沒有被設置且調用進程正在被調試,新進程將成爲調試調用進程的調試器的另外一個調試對象。若是調用進程沒有被調試,有關調試的行爲就不會產生。
(10)值:DETACHED_PROCESS
含義:對於控制檯進程,新進程沒有訪問父進程控制檯的權限。新進程能夠經過AllocConsole函數本身建立一個新的控制檯。這個標誌不能夠與CREATE_NEW_CONSOLE標誌一塊兒使用。
6.dwCreationFlags參數
還用來控制新進程的優先類,優先類用來決定此進程的線程調度的優先級。若是下面的優先級類標誌都沒有被指定,那麼默認的優先類是NORMAL_PRIORITY_CLASS,除非被建立的進程是IDLE_PRIORITY_CLASS。在這種狀況下子進程的默認優先類是IDLE_PRIORITY_CLASS。
能夠下面的標誌中的一個:
優先級:HIGH_PRIORITY_CLASS
含義:指示這個進程將執行時間臨界的任務,因此它必須被當即運行以保證正確。這個優先級的程序優先於正常優先級或空閒優先級的程序。一個例子是Windows任務列表,爲了保證當用戶調用時能夠馬上響應,放棄了對系統負荷的考慮。確保在使用高優先級時應該足夠謹慎,由於一個高優先級的CPU關聯應用程序能夠佔用幾乎所有的CPU可用時間。
優先級:IDLE_PRIORITY_CLASS
含義:指示這個進程的線程只有在系統空閒時纔會運行而且能夠被任何高優先級的任務打斷。例如屏幕保護程序。空閒優先級會被子進程繼承。
優先級:NORMAL_PRIORITY_CLASS
含義:指示這個進程沒有特殊的任務調度要求。
優先級:REALTIME_PRIORITY_CLASS
含義:指示這個進程擁有可用的最高優先級。一個擁有實時優先級的進程的線程能夠打斷全部其餘進程線程的執行,包括正在執行重要任務的系統進程。例如,一個執行時間稍長一點的實時進程可能致使磁盤緩存不足或鼠標反映遲鈍。
7.lpEnvironment:
指向一個新進程的環境塊。若是此參數爲空,新進程使用調用進程的環境。
一個環境塊存在於一個由以NULL結尾的字符串組成的塊中,這個塊也是以NULL結尾的。每一個字符串都是name=value的形式。
由於相等標誌被看成分隔符,因此它不能被環境變量看成變量名。
與其使用應用程序提供的環境塊,不如直接把這個參數設爲空,系統驅動器上的當前目錄信息不會被自動傳遞給新建立的進程。對於這個狀況的探討和如何處理,請參見注釋一節。
環境塊能夠包含Unicode或ANSI字符。若是lpEnvironment指向的環境塊包含Unicode字符,那麼dwCreationFlags字段的CREATE_UNICODE_ENVIRONMENT標誌將被設置。若是塊包含ANSI字符,該標誌將被清空。
請注意一個ANSI環境塊是由兩個零字節結束的:一個是字符串的結尾,另外一個用來結束這個快。一個Unicode環境塊是由四個零字節結束的:兩個表明字符串結束,另兩個用來結束塊。
8.lpCurrentDirectory:
指向一個以NULL結尾的字符串,這個字符串用來指定子進程的工做路徑。這個字符串必須是一個包含驅動器名的絕對路徑。若是這個參數爲空,新進程將使用與調用進程相同的驅動器和目錄。這個選項是一個須要啓動啓動應用程序並指定它們的驅動器和工做目錄的外殼程序的主要條件。
9.lpStartupInfo:
指向一個用於決定新進程的主窗體如何顯示的STARTUPINFO結構體。
10.lpProcessInformation:
指向一個用來接收新進程的識別信息的PROCESS_INFORMATION結構體。
編輯本段返回值
若是函數執行成功,返回非零值。
若是函數執行失敗,返回零,可使用GetLastError函數得到錯誤的附加信息。
註釋:
CreateProcess函數用來運行一個新程序。WinExec和LoadModule函數依舊可用,可是它們一樣經過調用CreateProcess函數實現。
另外CreateProcess函數除了建立一個進程,還建立一個線程對象。這個線程將連同一個已初始化了的堆棧一塊兒被建立,堆棧的大小由可執行文件的文件頭中的描述決定。線程由文件頭處開始執行。
新進程和新線程的句柄被以全局訪問權限建立。對於這兩個句柄中的任一個,若是沒有安全描述符,那麼這個句柄就能夠在任何須要句柄類型做爲參數的函數中被使用。當提供安全描述符時,在接下來的時候當句柄被使用時,老是會先進行訪問權限的檢查,若是訪問權限檢查拒絕訪問,請求的進程將不能使用這個句柄訪問這個進程。
這個進程會被分配給一個32位的進程標識符。直到進程停止這個標識符都是有效的。它能夠被用來標識這個進程,或在OpenProcess函數中被指定以打開這個進程的句柄。進程中被初始化了的線程同樣會被分配一個32位的線程標識符。這個標識符直到縣城停止都是有效的且能夠用來在系統中惟一標識這個線程。這些標識符在PROCESS_INFORMATION結構體中返回。
當在lpApplicationName或lpCommandLine參數中指定應用程序名時,應用程序名中是否包含擴展名都不會影響運行,只有一種狀況例外:一個以.com爲擴展名的MS-DOS程序或Windows程序必須包含.com擴展名。
調用進程能夠經過WaitForInputIdle函數來等待新進程完成它的初始化並等待用戶輸入。這對於父進程和子進程之間的同步是極其有用的,由於CreateProcess函數不會等待新進程完成它的初始化工做。舉例來講,在試圖與新進程關聯的窗口以前,進程應該先調用WaitForInputIdle。
首選的結束一個進程的方式是調用ExitProcess函數,由於這個函數通知這個進程的全部動態連接庫(DLLs)程序已進入結束狀態。其餘的結束進程的方法不會通知關聯的動態連接庫。注意當一個進程調用ExitProcess時,這個進程的其餘線程沒有機會運行其餘任何代碼(包括關聯動態連接庫的終止代碼)。
ExitProcess, ExitThread, CreateThread, CreateRemoteThread,當一個進程啓動時(調用了CreateProcess的結果)是在進程中序列化進行的。在一段地址空間中,同一時間內這些事件中只有一個能夠發生。這意味着下面的限制將保留:
*在進程啓動和DLL初始化階段,新的線程能夠被建立,可是直到進程的DLL初始化完成前它們都不能開始運行。
*在DLL初始化或卸下例程中進程中只能有一個線程。
*直到全部的線程都完成DLL初始化或卸下後,ExitProcess函數才返回。
在進程中的全部線程都終止且進程全部的句柄和它們的線程被經過調用CloseHandle函數終止前,進程會留在系統中。進程和主線程的句柄都必須經過調用CloseHandle函數關閉。若是再也不須要這些句柄,最好在建立進程後馬上關閉它們。
當進程中最後一個線程終止時,下列的事件發生:
*全部由進程打開的對象都會關閉。
*進程的終止狀態(由GetExitCodeProcess函數返回)從它的初始值STILL_ACTIVE變爲最後一個結束的線程的結束狀態。
*主線程的線程對象被設置爲標誌狀態,供其餘等待這個對象的線程使用。
*進程對象被設置爲標誌狀態,供其餘等待這個對象的線程使用。
假設當前在C盤上的目錄是\MSVC\MFC且有一個環境變量叫作C:,它的值是C:\MSVC\MFC,就像前面lpEnvironment中提到過的那樣,這樣的系統驅動器上的目錄信息在CreateProcess函數的lpEnvironment參數不爲空時不會被自動傳遞到新進程裏。一個應用程序必須手動地把當前目錄信息傳遞到新的進程中。爲了這樣作,應用程序必須直接建立環境字符串,並把它們按字母順序排列(由於Windows NT和Windows 95使用一種簡略的環境變量),並把它們放進lpEnvironment中指定的環境塊中。相似的,他們要找到環境塊的開頭,又要重複一次前面提到的環境塊的排序。
一種得到驅動器X的當前目錄變量的方法是調用GetFullPathName("x:",..)。這避免了一個應用程序必須去掃描環境塊。若是返回的絕對路徑是X:\,就不須要把這個值看成一個環境數據去傳遞了,由於根目錄是驅動器X上的新進程的默認當前目錄。
由CreateProcess函數返回的句柄對於進程對象具備PROCESS_ALL_ACCESS的訪問權限。
由lpcurrentDirectory參數指定的當前目錄室子進程對象的當前目錄。lpCommandLine參數指定的第二個項目是父進程的當前目錄。
對於Windows NT,當一個進程在指定了CREATE_NEW_PROCESS_GROUP的狀況下被建立時,一個對於SetConsoleCtrlHandler(NULL,True)的調用被用在新的進程上,這意味着對新進程來講CTRL+C是無效的。這使得上層的外科程序能夠本身處理CTRL+C信息並有選擇的把這些信號傳遞給子進程。CTRL+BREAK依舊有效,並可被用來中斷進程/進程樹的執行。
安全註釋:
第一個參數lpApplicationName多是空,這種狀況下,可執行文件的名字必須在lpCommandLine中,lpCommandLine參數中能夠包含空格。若是可執行文件或路徑中包含空格,那麼就會有執行不正確文件的風險,這是因爲這個函數解析空格的方法引發的。例如:下邊這個例子就很危險,由於它試圖運行Program.exe文件,若是這個文件存在,它就會代替MyApp.exe文件的運行。
CreateProcess(NULL,」C:\\Program Files\\MyApp.exe」,…….)
若是有惡意的用戶在系統編寫了一個名爲Program.exe的文件,那麼任何調用CreateProcess函數,且在文件路徑中使用Program Files文件夾的參數,都有可能會運行Program.exe文件,而不是運行原本打算運行的文件。
要避免這個問題,能夠不要將NULL值傳遞給lpApplicationName參數,或者在lpCommandLine中使用雙引號(轉義符)括起可執行文件的全路徑名,以下所示:
CreateProcess(NULL,」\」C:\\Program Files\\MyApp.exe\」 -L -S」,…….)
-L和-S是MyApp.exe可執行文件的參數。
最後要說明的一點是:在lpApplicationName中的參數和lpCommandLine中的第一個參數是同樣的,有人說顯得有些重複,其實這樣作純粹是一種被公認化了習慣!
參見
AllocConsole, CloseHandle, CreateRemoteThread, CreateThread, ExitProcess, ExitThread, GenerateConsoleCtrlEvent, GetCommandLine, GetEnvironmentStrings, GetExitCodeProcess, GetFullPathName, GetStartupInfo, GetSystemDirectory, GetWindowsDirectory, LoadModule, OpenProcess, PROCESS_INFORMATION, ResumeThread, SECURITY_ATTRIBUTES, SetConsoleCtrlHandler, SetErrorMode, STARTUPINFO, TerminateProcess, WaitForInputIdle, WaitForDebugEvent, WinExec
快捷信息:
導入庫:kernel32.lib
頭文件:Winbase.h
編輯本段舉例說明
C代碼
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
char szCommandLine[] = "notepad";
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW; // 指定wShowWindow成員有效
si.wShowWindow = TRUE; // 此成員設爲TRUE的話則顯示新建進程的主窗口
BOOL bRet = CreateProcess (
NULL, // 不在此指定可執行文件的文件名
szCommandLine, // 命令行參數
NULL, // 默認進程安全性
NULL, // 默認進程安全性
FALSE, // 指定當前進程內句柄不能夠被子進程繼承
CREATE_NEW_CONSOLE, // 爲新進程建立一個新的控制檯窗口
NULL, // 使用本進程的環境變量
NULL, // 使用本進程的驅動器和目錄
&si,
&pi) ;
if(bRet)
{
// 不使用的句柄最好關掉
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
printf("新進程的ID號:%d\n",pi.dwProcessId);
printf("新進程的主線程ID號:%d\n",pi.dwThreadId);
}
return 0;
}
C++代碼
#include <iostream>
#include<windows.h>
using namespace std;
int main()
{
STARTUPINFO si; //一些必備參數設置
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
PROCESS_INFORMATION pi; //必備參數設置結束
if(!CreateProcess("c:\\windows\\system32\\notepad.exe",NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
{
cout<<"Create Fail!"<<endl;
exit(1);
}
else
{
cout<<"Success!"<<endl;
}
return 0; }