複製鏈接
請複製以下鏈接發送給好友

CreateProcess

鎖定
WIN32API函數CreateProcess用來創建一個新的進程和它的主線程,這個新進程運行指定的可執行文件。
中文名
創建進程
外文名
CreateProcess
類    別
API函數
運    行
指定的可執行文件

CreateProcess函數原型

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

CreateProcess參數

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結構體。

CreateProcess返回值

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

CreateProcess註釋

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依舊有效,並可被用來中斷進程/進程樹的執行。

CreateProcess安全註釋

第一個參數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中的第一個參數是一樣的,有人説顯得有些重複,其實這樣做純粹是一種被公認化了習慣!

CreateProcess參見

CreateProcess快捷信息

導入庫kernel32.lib
頭文件:Winbase.h

CreateProcess舉例説明

CreateProcessC代碼

#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;
}

CreateProcessC++代碼

//不同的Windows系統notepad的路徑可能會有出入,改一下路徑即可(Win7x64親測通過)
#include <iostream>
#include <stdlib.h>
#include <windows.h>
using namespace std;
int main() {
  //一些必備參數設置
  STARTUPINFO si;
  memset(&si, 0,
         sizeof(STARTUPINFO)); //初始化si在內存塊中的值(詳見memset函數)
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_SHOW;
  PROCESS_INFORMATION pi; //必備參數設置結束
  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);
  return 0;
}

// MFC代碼

// 因為win7開始後更多的使用管理員權限,所以CreateProcessWithLogonW更多的被使用。
// 同樣的功能的還有CreateProcessAsUser和CreateProcessWithTokenW。

    //頭文件添加略去了(includeWindows.h),配合管道讀取回傳值

    //參考了MSDN相關條目,自行修改和DEBUG可用

    BOOL
    CreateMyProcess(CString strCommand, DWORD &dwReturn, CString &strLog,
                    CString strPwd) {
  HANDLE hToken;
  dwReturn = -1;
  s trLog = _T("");
  //嘗試登陸管理員賬户
  if (!LogonUserW(_T("Administrator"), NULL, s trPwd, LOGON32_LOGON_INTERACTIVE,
                  LOGON32_PROVIDER_DEFAULT, &hToken)) {
    intiError = GetLastError();
    strLog.Format(_T("ErrorOnLogonUserW(),errorcodeis%d."), iError);
    return FALSE;
  }
  BOOL blResult = FALSE;
  SECURITY_ATTRIBUTES sa;
  HANDLEhRead, hWrite;
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE; //創建管道
  if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
    strLog = _T("ErrorOnCreatePipe()");
    return FALSE;
  }
  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);
  return TRUE;
}