CreateProcess

CreateProcess

WIN32API函式CreateProcess用來創建一個新的進程和它的主執行緒,這個新進程運行指定的執行檔

基本介紹

  • 中文名:創建進程
  • 外文名:CreateProcess
  • 類別:API函式
  • 運行:指定的執行檔
函式原型,參數,返回值,注釋,安全注釋,參見,快捷信息,舉例說明,C代碼,C++代碼,

函式原型

BOOL CreateProcess(LPCTSTR lpApplicationName,LPTSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCTSTR lpCurrentDirectory,LPSTARTUPINFO lpStartupInfo,LPPROCESS_INFORMATIONlpProcessInformation);

參數

lpApplicationName
指向一個NULL結尾的、用來指定可執行模組的字元串。
這個字元串可以是可執行模組的絕對路徑,也可以是相對路徑,在後一種情況下,函式使用當前驅動器和目錄建立可執行模組的路徑。
這個參數可以被設為NULL,在這種情況下,可執行模組的名字必須處於 lpCommandLine 參數最前面並由空格符與後面的字元分開。
lpCommandLine
指向一個以NULL結尾的字元串,該字元串指定要執行的命令行。
這個參數可以為空,那么函式將使用lpApplicationName參數指定的字元串當做要運行的程式的命令行。
如果lpApplicationName和lpCommandLine參數都不為空,那么lpApplicationName參數指定將要被運行的模組,lpCommandLine參數指定將被運行的模組的命令行。新運行的進程可以使用GetCommandLine函式獲得整個命令行。C語言程式可以使用argc和argv參數。
lpProcessAttributes
指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄可以被子進程繼承。如果lpProcessAttributes參數為空(NULL),那么句柄不能被繼承。
在Windows NT中SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員指定了新進程的安全描述符,如果參數為空,新進程使用默認的安全描述符。
lpThreadAttributes
同lpProcessAttribute,不過這個參數決定的是執行緒是否被繼承.通常置為NULL.
bInheritHandles
指示新進程是否從調用進程處繼承了句柄。
如果參數的值為真,調用進程中的每一個可繼承的打開句柄都將被子進程繼承。被繼承的句柄與原進程擁有完全相同的值和訪問許可權。
dwCreationFlags
指定附加的、用來控制優先類和進程的創建的標誌。以下的創建標誌可以以除下面列出的方式外的任何方式組合後指定。
⑴值:CREATE_DEFAULT_ERROR_MODE
含義:新的進程不繼承調用進程的錯誤模式。CreateProcess函式賦予新進程當前的默認錯誤模式作為替代。應用程式可以調用SetErrorMode函式設定當前的默認錯誤模式。
這個標誌對於那些運行在沒有硬體錯誤環境下的多執行緒外殼程式是十分有用的。
對於CreateProcess函式,默認的行為是為新進程繼承調用者的錯誤模式。設定這個標誌以改變默認的處理方式。
⑵值:CREATE_NEW_CONSOLE
含義:新的進程將使用一個新的控制台,而不是繼承父進程的控制台。這個標誌不能與DETACHED_PROCESS標誌一起使用。
⑶值:CREATE_NEW_PROCESS_GROUP
含義:新進程將是一個進程樹的根進程。進程樹中的全部進程都是根進程的子進程。新進程樹的用戶標識符與這個進程的標識符是相同的,由lpProcessInformation參數返回。進程樹經常使用GenerateConsoleCtrlEvent函式允許傳送CTRL+C或CTRL+BREAK信號到一組控制台進程。
⑷值:CREATE_SEPARATE_WOW_VDM
如果被設定,新進程將會在一個私有的虛擬DOS機(VDM)中運行。另外,默認情況下所有的16位Windows應用程式都會在同一個共享的VDM中以執行緒的方式運行。單獨運行一個16位程式的優點是一個應用程式的崩潰只會結束這一個VDM的運行;其他那些在不同VDM中運行的程式會繼續正常的運行。同樣的,在不同VDM中運行的16位Windows應用程式擁有不同的輸入佇列,這意味著如果一個程式暫時失去回響,在獨立的VDM中的應用程式能夠繼續獲得輸入。
⑸值:CREATE_SHARED_WOW_VDM
如果WIN.INI中的Windows段的DefaultSeparateVDM選項被設定為真,這個標識使得CreateProcess函式越過這個選項並在共享的虛擬DOS機中運行新進程。
⑹值:CREATE_SUSPENDED
含義:新進程的主執行緒會以暫停的狀態被創建,直到調用ResumeThread函式被調用時才運行。
⑺值:CREATE_UNICODE_ENVIRONMENT
含義:如果被設定,由lpEnvironment參數指定的環境塊使用Unicode字元,如果為空,環境塊使用ANSI字元。
⑻值:DEBUG_PROCESS
含義:如果這個標誌被設定,調用進程將被當做一個調試程式,並且新進程會被當做被調試的進程。系統把被調試程式發生的所有調試事件通知給調試器。
如果你使用這個標誌創建進程,只有調用進程(調用CreateProcess函式的進程)可以調用WaitForDebugEvent函式。
⑼值:DEBUG_ONLY_THIS_PROCESS
含義:如果此標誌沒有被設定且調用進程正在被調試,新進程將成為調試調用進程的調試器的另一個調試對象。如果調用進程沒有被調試,有關調試的行為就不會產生。
⑽值:DETACHED_PROCESS
含義:對於控制台進程,新進程沒有訪問父進程控制台的許可權。新進程可以通過AllocConsole函式自己創建一個新的控制台。這個標誌不可以與CREATE_NEW_CONSOLE標誌一起使用。
〔11〕值:CREATE_NO_WINDOW
含義:系統不為新進程創建CUI視窗,使用該標誌可以創建不含視窗的CUI程式。
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
含義:指示這個進程擁有可用的最高優先權。一個擁有實時優先權的進程的執行緒可以打斷所有其他進程執行緒的執行,包括正在執行重要任務的系統進程。例如,一個執行時間稍長一點的實時進程可能導致磁碟快取不足或滑鼠反映遲鈍。
lpEnvironment
指向一個新進程的環境塊。如果此參數為空,新進程使用調用進程的環境。
一個環境塊存在於一個由以NULL結尾的字元串組成的塊中,這個塊也是以NULL結尾的。每個字元串都是name=value的形式。
因為相等標誌被當做分隔設定,所以它不能被環境變數當做變數名。
與其使用應用程式提供的環境塊,不如直接把這個參數設為空,系統驅動器上的當前目錄信息不會被自動傳遞給新創建的進程。對於這個情況的探討和如何處理,請參見注釋一節。
環境塊可以包含Unicode或ANSI字元。如果lpEnvironment指向的環境塊包含Unicode字元,那么dwCreationFlags欄位的CREATE_UNICODE_ENⅥRONMENT標誌將被設定。如果塊包含ANSI字元,該標誌將被清空。
請注意一個ANSI環境塊是由兩個零位元組結束的:一個是字元串的結尾,另一個用來結束這個快。一個Unicode環境塊是由四個零位元組結束的:兩個代表字元串結束,另兩個用來結束塊。
lpCurrentDirectory
指向一個以NULL結尾的字元串,這個字元串用來指定子進程的工作路徑。這個字元串必須是一個包含驅動器名的絕對路徑。如果這個參數為空,新進程將使用與調用進程相同的驅動器和目錄。這個選項是一個需要啟動應用程式並指定它們的驅動器和工作目錄的外殼程式的主要條件。
lpStartupInfo
指向一個用於決定新進程的主窗體如何顯示的STARTUPINFO結構體
lpProcessInformation
指向一個用來接收新進程的識別信息的PROCESS_INFORMATION結構體。

返回值

如果函式執行成功,返回非零值。
如果函式執行失敗,返回零,可以使用GetLastError函式獲得錯誤的附加信息。

注釋

CreateProcess函式用來運行一個新程式。WinExecLoadModule函式依舊可用,但是它們同樣通過調用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_ACTⅣE變為最後一個結束的執行緒的結束狀態。
*主執行緒的執行緒對象被設定為標誌狀態,供其他等待這個對象的執行緒使用。
*進程對象被設定為標誌狀態,供其他等待這個對象的執行緒使用。
假設當前在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中的第一個參數是一樣的,有人說顯得有些重複,其實這樣做純粹是一種被公認化了習慣!

參見

快捷信息

導入庫kernel32.lib
頭檔案:Winbase.h

舉例說明

C代碼

#include<stdio.h>#include<windows.h>intmain(intargc,char*argv[]){charszCommandLine[]="notepad";STARTUPINFOsi={sizeof(si)};PROCESS_INFORMATIONpi;si.dwFlags=STARTF_USESHOWWINDOW;//指定wShowWindow成員有效si.wShowWindow=TRUE;//此成員設為TRUE的話則顯示新建進程的主視窗BOOLbRet=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);}return0;}

C++代碼

//不同的Windows系統notepad的路徑可能會有出入,改一下路徑即可(Win7x64親測通過)#include<iostream>#include<stdlib.h>#include<windows.h>usingnamespacestd;intmain(){//一些必備參數設定STARTUPINFOsi;memset(&si,0,sizeof(STARTUPINFO));//初始化si在記憶體塊中的值(詳見memset函式)si.cb=sizeof(STARTUPINFO);si.dwFlags=STARTF_USESHOWWINDOW;si.wShowWindow=SW_SHOW;PROCESS_INFORMATIONpi;//必備參數設定結束if(!CreateProcess(TEXT("c:\\windows\\system32\\notepad.exe"),NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)){cout<<"CreateFail!"<<endl;exit(1);}else{cout<<"Success!"<<endl;}//不使用的句柄最好關掉CloseHandle(pi.hThread);CloseHandle(pi.hProcess);return0;}MFC代碼//因為win7開始後更多的使用管理員許可權,所以CreateProcessWithLogonW更多的被使用。同樣的功能的還有CreateProcessAsUser和CreateProcessWithTokenW。//頭檔案添加略去了(includeWindows.h),配合管道讀取回傳值//參考了MSDN相關條目,自行修改和DEBUG可用BOOLCreateMyProcess(CStringstrCommand,DWORD&dwReturn,CString&strLog,CStringstrPwd){HANDLEhToken;dwReturn=-1;strLog=_T("");//嘗試登入管理員賬戶if(!LogonUserW(_T("Administrator"),NULL,strPwd,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hToken)){intiError=GetLastError();strLog.Format(_T("ErrorOnLogonUserW(),errorcodeis%d."),iError);returnFALSE;}BOOLblResult=FALSE;SECURITY_ATTRIBUTESsa;HANDLEhRead,hWrite;sa.nLength=sizeof(SECURITY_ATTRIBUTES);sa.lpSecurityDescriptor=NULL;sa.bInheritHandle=TRUE;//創建管道if(!CreatePipe(&hRead,&hWrite,&sa,0)){strLog=_T("ErrorOnCreatePipe()");returnFALSE;}STARTUPINFOsi={sizeof(si)};//將cb成員初始化為sizeof(si),其他成員初始化為0::GetStartupInfo(&si);si.hStdError=hWrite;si.hStdOutput=hWrite;si.wShowWindow=SW_HIDE;si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;PROCESS_INFORMATIONpi;CStringstrInfo=_T("");ZeroMemory(&pi,sizeof(pi));//管理員方式啟動進程if(!CreateProcessWithLogonW(_T("Administrator"),NULL,strPwd,LOGON_WITH_PROFILE,NULL,strCommand.GetBuffer(),CREATE_UNICODE_ENVIRONMENT,NULL,NULL,&si,&pi)){intiError=GetLastError();strLog.Format(_T("ErrorOnCreateProcessWithLogonW(),errorcodeis%d."),iError);CloseHandle(hWrite);CloseHandle(hRead);returnFALSE;}CloseHandle(hWrite);charbuffer[4096]={0};DWORDbytesRead=0;//讀取回傳值while(true){if(ReadFile(hRead,buffer,4095,&bytesRead,NULL)==NULL)break;//獲取了回傳值,處理回傳值Sleep(100);}//資源清理CloseHandle(hRead);CloseHandle(hToken);WaitForSingleObject(pi.hProcess,INFINITE);GetExitCodeProcess(pi.hProcess,&dwReturn);CloseHandle(pi.hProcess);CloseHandle(pi.hThread);returnTRUE;}

相關詞條

熱門詞條

聯絡我們